JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptで2つの文字列を比較して類似度(一致率)をパーセントで返す方法

はじめに

JavaScriptでは、2つの文字列を比較して「どのくらい似ているか」をパーセンテージで返す関数を実装できます。こうした類似度の判定は、検索候補の表示、フォーム入力の補完、データの重複チェック、簡易的なスペルチェッカーなど、さまざまな場面で役立ちます。

ここでいう類似度とは、2つの文字列が互いにどれだけ多くの文字を共有しているかを表す指標です。今回作成する関数は、次の仕様を満たします。

  • 2つの文字列が完全に一致していれば 100 を返す
  • 共通する文字がひとつもなければ 0 を返す
  • 大文字・小文字の違いは区別しない
  • 結果は小数第2位まで丸めた数値で返す

アルゴリズムの鍵:レーベンシュタイン距離

文字列同士の近さを測る定番の手法が「レーベンシュタイン距離(編集距離)」です。これは、片方の文字列をもう片方に変換するまでに必要な、文字の挿入・削除・置換の最小回数を求めるアルゴリズムで、距離が小さいほど2つの文字列は似ていることを意味します。

編集距離が求まれば、次の式で類似度をパーセンテージに変換できます。

類似度(%) = (長い方の文字数 − 編集距離) ÷ 長い方の文字数 × 100

サンプルコード

2つの文字列を受け取り、類似度(%)を返す関数の実装例がこちらです。

const calculateSimilarity = (str1 = '', str2 = '') => {
  // 文字数の長い方を基準にする
  let longer = str1;
  let shorter = str2;
  if (str1.length < str2.length) {
    longer = str2;
    shorter = str1;
  }
  const longerLength = longer.length;

  // 両方が空文字列の場合は完全一致として100を返す
  if (longerLength === 0) {
    return 100;
  }

  // 小数第2位まで丸めた類似度(%)を返す
  return +((longerLength - matchDestructively(longer, shorter)) / parseFloat(longerLength) * 100).toFixed(2);
};

// レーベンシュタイン距離(編集距離)を計算する
const matchDestructively = (str1 = '', str2 = '') => {
  // 大文字・小文字の違いは無視する
  str1 = str1.toLowerCase();
  str2 = str2.toLowerCase();

  const arr = [];
  for (let i = 0; i <= str1.length; i++) {
    let lastValue = i;
    for (let j = 0; j <= str2.length; j++) {
      if (i === 0) {
        arr[j] = j;
      } else if (j > 0) {
        let newValue = arr[j - 1];
        if (str1.charAt(i - 1) !== str2.charAt(j - 1)) {
          newValue = Math.min(Math.min(newValue, lastValue), arr[j]) + 1;
        }
        arr[j - 1] = lastValue;
        lastValue = newValue;
      }
    }
    if (i > 0) {
      arr[str2.length] = lastValue;
    }
  }
  return arr[str2.length];
};

console.log(calculateSimilarity('Mathematics', 'Mathamatecs'));
// 出力: 81.82

コードの解説

calculateSimilarity 関数

  • 引数として受け取った2つの文字列を、文字数の長い方(longer)と短い方(shorter)に振り分けます。これにより、渡す順序にかかわらず常に同じ結果になります。
  • 長い方の文字数が0、つまり両方が空文字列の場合は完全一致とみなし 100 を返します。
  • 最後に、先述の式に沿って「長さ − 編集距離」を全体の長さで割り、100倍して小数第2位で丸めた値を返します。

matchDestructively 関数(編集距離の計算)

  • 大文字・小文字の違いによる誤差を防ぐため、比較前に両方の文字列を toLowerCase() で小文字化しています。
  • 編集距離は動的計画法(DP)で求めており、1次元配列を順次更新することでメモリ使用量を抑えています。計算量は時間 O(m × n)、空間 O(n) と効率的です。

実行結果

たとえば「Mathematics」と「Mathamatecs」を比較してみましょう。2つの文字列は e→a と i→e の2文字分だけ異なるため、編集距離は2、長い方の文字数は11となり、次の結果が出力されます。

81.82

このように、つづりが少し違う程度の文字列ペアでは80〜90%台の高い類似度が得られます。一方、まったく共通点のない文字列であれば0に近い値が返ります。また、calculateSimilarity('Hello World', 'hello world') のように大文字・小文字だけが違う場合は、小文字化によって100(完全一致)が返される点にも注目してください。

まとめ

レーベンシュタイン距離を利用すれば、外部ライブラリに頼らずともJavaScriptだけで文字列の類似度をパーセンテージとして算出できます。軽量なファジー検索や入力支援機能の土台として手軽に導入できるので、ぜひ活用してみてください。より高精度なマッチングが必要になった場合は、Dice係数(バイグラム比較)やジャロ・ウィンクラー距離といった別のアルゴリズムとの使い分けも検討するとよいでしょう。

  1. JavaScriptで現在のロケールに基づいて2つの文字列を比較する方法

    localeCompare()メソッドとはJavaScriptで現在のロケール(言語・地域設定)に基づいて2つの文字列を比較するには、localeCompare()メソッドを使用します。このメソッドは、比較結果に応じて以下の数値を返します。0:2つの文字列が等しい場合-1:string1 がソート順で string2 よりも前に来る場合1:string1 がソート順で string2 よりも後に来る場合使用例以下のコードを実行して、JavaScriptで現在のロケールに基づいて2つの文字列を比較する方法を確認してみましょう。<!DOCTYPE html> <html>

  2. JavaScriptのテンプレート文字列(テンプレートリテラル)とは?基本の使い方を解説

    テンプレート文字列(テンプレートリテラル)とは テンプレート文字列は、ES6(ECMAScript 2015)で導入された機能で、文字列の中に式を直接埋め込むことを可能にしました。従来のシングルクォート()やダブルクォート()の代わりに、バッククォート(``)を使用する点が特徴です。 テンプレート文字列を使うと、文字列の連結や補間(インターポレーション)がはるかに直感的に行えます。例えば ${a + b} のように記述することで、文字列の中に任意の式をそのまま埋め込めます。従来のように + 演算子で文字列をつなぎ合わせる方法と比べると、はるかに読みやすく美しい構文です。 JavaScriptに