SCALE — Build Lab
機能パターン · REACT COMPONENT

重複検知(ファジーマッチ)

CATEGORY機能パターン TYPEReact Component EFFORT60〜120分 DIFFICULTY
PRIMARY CODE
ts
// 文字列正規化
function normalize(s: string) {
  return s
    .toLowerCase()
    .replace(/[\((]株[\))]/g, '株式会社')
    .replace(/\s+/g, '')
    .normalize('NFKC');
}

// Levenshtein距離
function distance(a: string, b: string): number {
  const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
  for (let i = 0; i <= a.length; i++) dp[i][0] = i;
  for (let j = 0; j <= b.length; j++) dp[0][j] = j;
  for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
    dp[i][j] = a[i - 1] === b[j - 1]
      ? dp[i - 1][j - 1]
      : Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1;
  }
  return dp[a.length][b.length];
}

export function findDuplicates(items: { id: string; name: string }[], threshold = 2) {
  const groups: string[][] = [];
  items.forEach(it => {
    const norm = normalize(it.name);
    const g = groups.find(g => g.some(id => {
      const other = items.find(x => x.id === id)!;
      return distance(normalize(other.name), norm) <= threshold;
    }));
    if (g) g.push(it.id); else groups.push([it.id]);
  });
  return groups.filter(g => g.length > 1);
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

重複検知(ファジーマッチ)

:LiTarget: 用途

会社名・メアドの表記揺れを検出。「株式会社SCALE」「(株)SCALE」を同一視。

:LiSparkle: 特徴

  • Levenshtein距離
  • 正規化(全/半角・カタカナ等)
  • クラスタリング
  • 統合候補提示

:LiCode: コード(コピペ用)

// 文字列正規化
function normalize(s: string) {
  return s
    .toLowerCase()
    .replace(/[\((][\))]/g, '株式会社')
    .replace(/\s+/g, '')
    .normalize('NFKC');
}

// Levenshtein距離
function distance(a: string, b: string): number {
  const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
  for (let i = 0; i <= a.length; i++) dp[i][0] = i;
  for (let j = 0; j <= b.length; j++) dp[0][j] = j;
  for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
    dp[i][j] = a[i - 1] === b[j - 1]
      ? dp[i - 1][j - 1]
      : Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1;
  }
  return dp[a.length][b.length];
}

export function findDuplicates(items: { id: string; name: string }[], threshold = 2) {
  const groups: string[][] = [];
  items.forEach(it => {
    const norm = normalize(it.name);
    const g = groups.find(g => g.some(id => {
      const other = items.find(x => x.id === id)!;
      return distance(normalize(other.name), norm) <= threshold;
    }));
    if (g) g.push(it.id); else groups.push([it.id]);
  });
  return groups.filter(g => g.length > 1);
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加