何をしたいユーティリティか:「インデックス検索」
「インデックス検索」は、配列の中から「条件に合う要素がどこにあるか」を調べて、その位置(インデックス)を返す処理です。
「この値は何番目にある?」「id が一致する要素はどこ?」「最初に条件を満たす要素の位置は?」といった問いに答えるためのユーティリティです。
業務だと、例えばこういう場面で使います。
特定のユーザーを配列から探して、その位置に対して削除・置換・挿入をしたい。
選択中リストの中で、ある ID が何番目かを知りたい。
「最初にエラーになった行」のインデックスを取りたい。
ここで大事なのは、「見つからなかったときどうするか」と「何を基準に探すか」を、ユーティリティとして決めておくことです。
基本形1:値で探す indexOf をラップする findIndexOf
プリミティブ値を探す一番シンプルな形
まずは、「この値が配列のどこにあるか」を調べる基本形です。
function findIndexOf(array, value) {
if (!Array.isArray(array)) {
return -1;
}
return array.indexOf(value);
}
JavaScriptここでのポイントは、
配列じゃなければ「見つからない」を表す -1 を返す。indexOf は「最初に一致した位置」を返し、見つからなければ -1 を返す。
というシンプルなルールです。
動作例
const arr = ["a", "b", "c", "b"];
findIndexOf(arr, "a"); // 0
findIndexOf(arr, "b"); // 1(最初の b)
findIndexOf(arr, "x"); // -1
findIndexOf(null, "a"); // -1
JavaScript「値がどこにあるか」を知りたいだけなら、このレベルで十分です。
基本形2:条件で探す findIndex(業務で一番使う形)
predicate(条件関数)で「探す基準」を決める
業務では、「id が一致する要素」「フラグが true の要素」「スコアが一定以上の最初の要素」など、
単純な値一致ではなく「条件」で探したいことが多いです。
そのときに使うのが Array.prototype.findIndex で、それをユーティリティとしてラップした形がこちらです。
function findIndexBy(array, predicate) {
if (!Array.isArray(array)) {
return -1;
}
if (typeof predicate !== "function") {
return -1;
}
return array.findIndex(predicate);
}
JavaScriptここでは、
配列じゃなければ -1。
predicate が関数でなければ -1。
それ以外は findIndex にそのまま渡す。
というルールにしています。
動作例:id が一致する要素のインデックスを探す
const users = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 3, name: "C" },
];
const index = findIndexBy(users, (u) => u.id === 2);
// index = 1
const notFound = findIndexBy(users, (u) => u.id === 99);
// notFound = -1
JavaScript「id=2 のユーザーは何番目か?」という問いに、findIndexBy で答えられます。
インデックス検索を他のユーティリティと組み合わせる
見つけた位置に対して削除・置換・挿入をする
インデックス検索は、それ単体で使うよりも「次の操作のための下準備」として使うことが多いです。
例えば、「id が一致する要素を削除する」ならこう書けます。
function removeById(array, id) {
const index = findIndexBy(array, (item) => item.id === id);
if (index === -1) {
return Array.isArray(array) ? array.slice() : [];
}
return removeAt(array, index); // 以前作った removeAt を使う
}
JavaScript「id が一致する要素を更新する」ならこうです。
function updateById(array, id, updater) {
const index = findIndexBy(array, (item) => item.id === id);
if (index === -1 || typeof updater !== "function") {
return Array.isArray(array) ? array.slice() : [];
}
const target = array[index];
const updated = updater(target);
return replaceAt(array, index, updated); // 以前作った replaceAt を使う
}
JavaScriptここでの重要ポイントは、
インデックス検索は「どこを触るか」を決めるための道具。
実際の削除・置換は、別のユーティリティ(removeAt / replaceAt)に任せる。
という役割分担です。
「見つからなかったとき」の扱いを決めておく
-1 をそのまま返すか、例外にするか、何もしないか
indexOf や findIndex は、見つからないときに -1 を返します。
ユーティリティでも基本はそれに乗っかりますが、「その後どう扱うか」を決めておくことが大事です。
例えば、
削除系なら「-1 のときは何もしないでコピーだけ返す」。
更新系なら「-1 のときは元の配列をそのまま返す」。
挿入系なら「-1 のときは末尾に挿入する」など。
インデックス検索ユーティリティ自体は「-1 を返すだけ」にしておき、
呼び出し側(削除・更新ユーティリティ)で「-1 のときの振る舞い」を決める、という設計が読みやすくなります。
業務でありがちなパターンをユーティリティにしておく
「存在チェック」と「インデックス取得」を分ける
よくあるのが、「その要素が存在するかどうか」と「どこにあるか」を両方知りたいパターンです。
function hasBy(array, predicate) {
return findIndexBy(array, predicate) !== -1;
}
JavaScriptこうしておくと、
「存在するかどうかだけ知りたい」 → hasBy
「どこにあるかも知りたい」 → findIndexBy
と使い分けられます。
「なければ末尾に挿入する」ようなパターン
例えば、「id が一致する要素があれば更新、なければ末尾に追加」という業務ロジック。
function upsertById(array, item) {
if (!Array.isArray(array) || !item || typeof item.id === "undefined") {
return Array.isArray(array) ? array.slice() : [];
}
const index = findIndexBy(array, (x) => x.id === item.id);
if (index === -1) {
return [...array, item]; // 末尾に追加
}
return replaceAt(array, index, item); // 既存を置換
}
JavaScriptここでも、「インデックス検索」が「更新か追加かを決めるための判断材料」として使われています。
手を動かしてインデックス検索の感覚をつかむ
コンソールで、次のコードを実際に打ってみてください。
function findIndexOf(array, value) {
if (!Array.isArray(array)) return -1;
return array.indexOf(value);
}
function findIndexBy(array, predicate) {
if (!Array.isArray(array)) return -1;
if (typeof predicate !== "function") return -1;
return array.findIndex(predicate);
}
const arr = ["a", "b", "c", "b"];
console.log(findIndexOf(arr, "b")); // 1
console.log(findIndexOf(arr, "x")); // -1
const users = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 3, name: "C" },
];
console.log(findIndexBy(users, (u) => u.id === 2)); // 1
console.log(findIndexBy(users, (u) => u.name === "Z")); // -1
JavaScript「どの条件で何番目が返ってくるか」「見つからないときはどうなるか」を、自分の目で確認してみてください。
まとめ:インデックス検索は“次の操作のための座標を取る”道具
インデックス検索ユーティリティは、それ単体で完結するというより、
削除ユーティリティに「どこを消すか」を教える。
置換ユーティリティに「どこを更新するか」を教える。
挿入ユーティリティに「どこに差し込むか」を教える。
という、「座標を取るための道具」です。
プロジェクトに
export function findIndexOf(...) { ... }
export function findIndexBy(...) { ... }
export function hasBy(...) { ... }
JavaScriptのような関数を置き、「配列の“どこ”を触るか決めたいときは必ずこれを通す」と決めておくと、
配列操作のロジックがきれいに分離されて、読みやすさもバグ耐性も一気に上がります。
