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で処理を書かないといけないのかな...と思っていたんですが、調べてみたら文字列にも「正規化」の概念が存在するんですね!
大変便利だったので、紹介させていただきました。皆さんも文字列の「正規化」をしてシンプルに実装しましょう。


