「部分一致検索」ユーティリティは何をしてくれるのか
業務システムで文字列を扱うとき、「完全一致」だけで足りることはあまりありません。
ユーザー一覧から「山」で始まる人を探したい、
商品名に「USB」が含まれるものだけを絞り込みたい、
ログメッセージの中に特定のキーワードが含まれているか調べたい。
こういうときに使うのが 「部分一致検索」 です。
「文字列Aの中に、文字列Bが“含まれているか”を調べる」ための、小さなユーティリティを用意しておくと、
検索・フィルタ・バリデーションが一気に書きやすくなります。
JavaScript 標準の基本:includes と indexOf
includes の基本
一番シンプルな部分一致検索は、String.prototype.includes です。
const text = "USBメモリ 32GB";
text.includes("USB"); // true
text.includes("メモリ"); // true
text.includes("HDD"); // false
JavaScript「text の中に "USB" が含まれているか?」を true / false で返してくれます。
これだけでも、簡単なフィルタ機能なら十分作れます。
const products = [
"USBメモリ 32GB",
"外付けHDD 1TB",
"USB-C ケーブル",
];
const keyword = "USB";
const result = products.filter((name) => name.includes(keyword));
// ["USBメモリ 32GB", "USB-C ケーブル"]
JavaScriptindexOf の基本
includes がない古い環境や、「位置」も知りたいときは indexOf を使います。
const text = "USBメモリ 32GB";
text.indexOf("USB"); // 0(先頭)
text.indexOf("メモリ"); // 3(4文字目)
text.indexOf("HDD"); // -1(見つからない)
JavaScript-1 かどうかで「含まれているか」を判定できます。
if (text.indexOf("USB") !== -1) {
// "USB" を含んでいる
}
JavaScript実務で必要になる「ひと工夫」
現場で部分一致検索を使うとき、
そのまま includes を呼ぶだけでは足りないことが多いです。
よくある要望は、だいたいこんな感じです。
大文字・小文字を区別したくない(”usb” でも “USB” でもヒットしてほしい)。
全角・半角の違いを無視したい(”USB” でも “USB” でもヒットしてほしい)。
前後のスペースや余計な空白は無視したい。
これを毎回バラバラに書くと、すぐにコードがぐちゃぐちゃになります。
なので、「検索用に正規化してから includes する」ユーティリティを一つ作っておくと、とても楽になります。
部分一致検索ユーティリティの基本形
大文字・小文字を無視するバージョン
まずは「大文字・小文字を区別しない」シンプルなユーティリティから。
function includesIgnoreCase(text, keyword) {
if (text == null || keyword == null) return false;
const t = String(text).toLowerCase();
const k = String(keyword).toLowerCase();
if (k === "") return true; // 空キーワードは「常に含まれる」とみなす
return t.includes(k);
}
JavaScriptポイントを噛み砕きます。
null / undefined が来ても落ちないようにしている。String(...) で文字列化してから toLowerCase() で小文字にそろえる。
キーワードが空文字のときは true を返す(検索 UI では自然な挙動)。
最終的には includes で部分一致を判定する。
使い方はこうです。
includesIgnoreCase("USBメモリ", "usb"); // true
includesIgnoreCase("usbメモリ", "USB"); // true
includesIgnoreCase("外付けHDD", "usb"); // false
JavaScript全角・半角も吸収するバージョン
日本語環境だと、「全角・半角の揺れ」もよく問題になります。
例えば "USB" と "USB" を同じものとして扱いたい。
簡易的には、「英数字を半角にそろえる」ユーティリティを組み合わせます。
function toHalfWidth(str) {
if (str == null) return "";
return String(str)
.replace(/[!-~]/g, (ch) =>
String.fromCharCode(ch.charCodeAt(0) - 0xFEE0)
)
.replace(/ /g, " ");
}
function normalizeForSearch(str) {
return toHalfWidth(str).toLowerCase().trim();
}
function includesLoose(text, keyword) {
if (text == null || keyword == null) return false;
const t = normalizeForSearch(text);
const k = normalizeForSearch(keyword);
if (k === "") return true;
return t.includes(k);
}
JavaScriptこれで、次のような検索ができます。
includesLoose("USBメモリ", "usb"); // true
includesLoose("usbメモリ", "USB"); // true
includesLoose(" USB メモリ ", "usb"); // true(前後の空白も無視)
JavaScriptここでの重要ポイントは、
「検索用の正規化」を normalizeForSearch に閉じ込めていること。
呼び出し側は「どう正規化しているか」を意識せずに includesLoose だけ使えばいいこと。
です。
業務での具体的な使いどころ
一覧画面の簡易検索・絞り込み
例えば、ユーザー一覧の簡易検索を考えます。
const users = [
{ name: "山田太郎", email: "taro@example.com" },
{ name: "佐藤花子", email: "hanako@example.com" },
{ name: "山本一郎", email: "ichiro@example.com" },
];
function filterUsers(users, keyword) {
return users.filter((user) => {
return (
includesLoose(user.name, keyword) ||
includesLoose(user.email, keyword)
);
});
}
filterUsers(users, "山"); // 山田太郎・山本一郎がヒット
filterUsers(users, "EXAMPLE"); // 全員ヒット(大文字小文字無視)
JavaScript「名前かメールアドレスのどちらかに部分一致したら表示する」という、
よくある検索 UI を、かなりシンプルに書けます。
バリデーションや警告メッセージの判定
ログメッセージやエラーメッセージの中に、
特定のキーワードが含まれているかどうかで挙動を変えたいこともあります。
function isTimeoutError(message) {
return includesIgnoreCase(message, "timeout");
}
isTimeoutError("Request Timeout"); // true
isTimeoutError("connection TIMEOUT"); // true
isTimeoutError("Network error"); // false
JavaScript「完全一致ではないけれど、“この単語が含まれていたらタイムアウト扱い”」
といった判定を、簡単に表現できます。
設計として意識してほしいこと
「検索用の正規化」を一箇所にまとめる
一番大事なのは、
「検索前にどう正規化するか」をユーティリティに閉じ込めることです。
大文字・小文字をそろえるのか。
全角・半角をそろえるのか。
前後の空白を削るのか。
これを画面ごと・機能ごとにバラバラに書き始めると、
挙動が微妙に違う検索が量産されて、ユーザー体験もテストも地獄になります。
なので、
normalizeForSearch のような関数を一つ決める。
「部分一致検索は必ずそれを通してからやる」とルール化する。
この二つをやるだけで、
検索まわりのコードの一貫性と保守性が一気に上がります。
「どこまで緩くするか」は仕様として決める
includesLoose のような「ゆるい検索」は便利ですが、
緩くしすぎると意図しないヒットも増えます。
例えば、
数字の全角・半角も無視するか。
ひらがな・カタカナの違いも無視するか。
記号を全部無視するか。
など、「どこまでを同じとみなすか」は、
プロダクトごとにちゃんと決めておく必要があります。
ユーティリティを作るときは、
「この関数は、何を無視して、何を区別するのか」
をコメントや名前で明確にしておくと、
後から見た人にも意図が伝わりやすくなります。
小さな練習で感覚をつかむ
コンソールで、いくつか試してみてください。
"USBメモリ".includes("USB");
"USBメモリ".includes("usb");
includesIgnoreCase("USBメモリ", "usb");
includesLoose("USBメモリ", "usb");
includesLoose(" usb メモリ ", "USB");
JavaScriptどこからが true になって、
どこからが false になるのかを体で覚えていくと、
「検索用の正規化ってこういうことか」という感覚がつかめます。
そのうえで、自分のプロジェクトに
export function normalizeForSearch(str) { ... }
export function includesLoose(text, keyword) { ... }
JavaScriptのようなユーティリティを一つ置いて、
「検索・絞り込み・判定で部分一致を使うときは必ずここを通す」
というルールにしてみてください。
それができた瞬間、あなたの検索処理は
「その場しのぎの includes の寄せ集め」から
「設計された検索体験」に、一段レベルアップします。

