本文へ移動

JavaScriptのnormalizeで文字列の「正規化」をして、検索機能の半角・全角の差をシンプルに吸収する

検索機能を実装する際、ユーザーの入力する文字列の全角・半角の違いはよくある課題だと思います。「株式会社アイウエオ」と検索したいユーザーが誤って「株式会社アイウエオ」と半角カナで入力した場合でも、正しく検索結果を返す必要がありますよね。

また、登録データ自体が「株式会社アイウエオ」と「株式会社アイウエオ」のように全角カナと半角カナや、「株式会社ABCD」と「株式会社ABCD」のように全角英数字と半角英数字が混在しているケースも考えられます...。

normalize で文字列を「正規化」する

normalize() メソッドは、文字列の Unicode 正規化形式を返します✅

引数として、以下の4つの Unicode 正規化形式を指定できるようになっていて、検索機能では、NFKCの形式が推奨されます。

  • NFC : 正規化形式 C。正準等価性によって分解され、再度合成される。
  • NFD : 正規化形式 D。正準等価性によって分解される。
  • NFKC : 正規化形式 KC。互換等価性によって分解され、正準等価性によって再度合成される。
  • NFKD : 正規化形式 KD。互換等価性によって分解される。

NFKCの形式で文字列を正規化すると、半角カナを全角カナに、全角英数字を半角英数字に変換されるため、幅広い表記揺れを吸収できます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/normalize

検索機能で文字列の半角・全角の差を吸収する実装例

検索機能でのシンプルな実装例です✅

/**
 * 配列から検索キーワードに一致する要素を抽出する関数
 * @param data - 検索対象の文字列配列
 * @param keyword - ユーザーが入力した検索キーワード
 * @returns 検索結果の配列
 */
const searchWithNormalization = (data: string[], keyword: string): string[] => {
  // 検索キーワードをNFKC形式で正規化する
  const normalizedKeyword = keyword.normalize("NFKC");

  // 配列の各要素も正規化してキーワードと比較する
  return data.filter((item: string): boolean => {
    const normalizedItem = item.normalize("NFKC");
    return normalizedItem.includes(normalizedKeyword);
  });
};

// 登録されているデータ
const companyList = [
  '株式会社アイウエオ', // 全角カナ
  '株式会社アイウエオ', // 半角カタカナ
  '株式会社ABCD', // 全角英数
  '株式会社ABCD', // 半角英数
];

// 実行例: ユーザーが全角カナで検索した場合
const result1 = searchWithNormalization(companyList, '株式会社アイウエオ');
console.log(result1); // ['株式会社アイウエオ', '株式会社アイウエオ']

// 実行例: ユーザーが半角カナで検索した場合
const result2 = searchWithNormalization(companyList, '株式会社アイウエオ');
console.log(result2); // ['株式会社アイウエオ', '株式会社アイウエオ']

// 実行例: ユーザーが全角英数で検索した場合
const result3 = searchWithNormalization(companyList, '株式会社ABCD');
console.log(result3); // ['株式会社ABCD', '株式会社ABCD']

// 実行例: ユーザーが半角英数で検索した場合
const result4 = searchWithNormalization(companyList, '株式会社ABCD');
console.log(result4); // ['株式会社ABCD', '株式会社ABCD']

// 全ての例でユーザーの入力する文字列が全角・半角の場合でもヒットしている⭕️

Unicode 正規化形式について

Unicode 正規化形式は、「見た目は同じでも内部データが異なる文字」を統一するための仕組みです。NFKCの形式は半角・全角だけでなく、以下の文字の表記揺れも吸収できます。

  • アクセント記号
    「é」は2つの表現方法があり、見た目は同じですが、内部のデータは異なる文字です。
    "é"    // é (1文字)
    "é"    // \e\u0301 (e + アクセント記号)
    
    // 検索で両方ヒットさせたい場合NFKC形式で正規化すると吸収できる
    "é".normalize('NFKC') === "é".normalize('NFKC') // true
    
  • 丸数字・特殊文字
    丸数字や特殊文字は通常文字に変換されます。
    "①②③".normalize('NFKC') // '123'
    "㈱アイウエオ".normalize('NFKC') // '(株)アイウエオ'
    "㍿アイウエオ".normalize('NFKC') // '(株)アイウエオ'

まとめ

最初、半角と全角の表記揺れの吸収は正規表現とreplaceで処理を書かないといけないのかな...と思っていたんですが、調べてみたら文字列にも「正規化」の概念が存在するんですね!

大変便利だったので、紹介させていただきました。皆さんも文字列の「正規化」をしてシンプルに実装しましょう。

くりちゃん

東京で働くクリエイティブ・フロントエンドデベロッパー。
プログラミングが好きで休みの日もコードを書いて、モノづくりを楽しんでいる。文章を書くのは苦手。

FOLLOW

PR