jQuery / メソッド / jQuery.inArray() - 配列の位置を正しく検索する

jQuery.inArray(value, array, fromIndex)は、配列で最初に一致したインデックスを返し、見つからなければ-1を返します。存在判定にはindex !== -1を使います。0は最初の位置で見つかった意味なので、戻り値をそのままif条件にすると誤判定します。

戻り値は真偽値ではなく位置

jQuery.inArray()の仕様では、検索値・配列・省略可能な開始インデックスの順に渡します。jQueryのグローバルユーティリティで、$.inArray(...)やjQuery.inArray(...)と呼びます。$(selector).inArray(...)のようにDOM選択へつなぐメソッドではありません。

戻り値 意味 Boolean(index) index !== -1
0 最初の項目で一致 false true
1以上の整数 その位置で一致 true true
-1 検索範囲で見つからない true false

if (index)やindex > 0は最初の項目を見落とします。-1も条件ではtrueになるため、単に否定しても解決しません。-1と明示比較するか、0以上かを確認します。

型と開始位置を変える完全な例

全コードをUTF-8のinarray-demo.htmlとして保存し、ブラウザーで開きます。コンテンツ配信ネットワーク(CDN)から公式jQueryファイル4.0.0を読むインターネット接続が必要ですが、インストールは不要です。数値・文字列、正負の開始位置、空配列と以下の比較を3.7.1と4.0.0で実行しました。

配列は[0, "0", "alpha", 0]です。Valueは定義済みの選択肢から取り、文字列式を評価しません。From indexだけをNumber()で開始位置へ変換します。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Search an array with inArray</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
    button, select { font: inherit; margin: 4px; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <h1>Find an index rather than a boolean</h1>
  <p>Array: [0, "0", "alpha", 0]</p>
  <label for="value">Value:</label>
  <select id="value"><option value="number">0 (number)</option><option value="string">0 (string)</option><option value="alpha">alpha</option><option value="missing">missing</option></select>
  <label for="from">From index:</label>
  <select id="from"><option>0</option><option>1</option><option>3</option><option>9</option><option>-1</option></select>
  <button type="button" id="search">Search</button>
  <button type="button" id="reset">Reset</button>
  <pre id="result" aria-live="polite"></pre>
  <script>
    $(function () {
      const array = [0, "0", "alpha", 0];
      const choices = { number: 0, string: "0", alpha: "alpha", missing: "missing" };
      function search() {
        const value = choices[$("#value").val()];
        const fromIndex = Number($("#from").val());
        const index = $.inArray(value, array, fromIndex);
        const found = index !== -1;
        $("#result").text(`value=${JSON.stringify(value)} (${typeof value})\nindex=${index}\nfound=${found}\nBoolean(index)=${Boolean(index)}`);
      }
      $("#value, #from").on("change", search);
      $("#search").on("click", search);
      $("#reset").on("click", function () {
        $("#value").val("number");
        $("#from").val("0");
        search();
      });
      search();
    });
  </script>
</body>
</html>

初期状態では数値0が先頭に見つかります。

value=0 (number)
index=0
found=true
Boolean(index)=false
  1. 0 (string)を選ぶとindexは1です。数値0と文字列"0"を区別します。
  2. 0 (number)に戻し、From indexを1にすると3です。最初の数値0を飛ばします。
  3. 3も開始位置を含むため3を返します。
  4. 9は配列長を超え、index -1、found falseですがBoolean(index)はtrueです。
  5. -1は末尾から一つ目のインデックス3で始まり、数値0が見つかります。
  6. 開始を0にしてalphaとmissingを選ぶと2と-1です。Resetは数値0・開始0に戻し、Searchは現在条件で再検索します。

厳密比較とfromIndexの範囲

比較は===のような厳密比較です。"0"を数値0へ自動変換せず、部分文字列にも一致しません。入力値を文字列か数値のどちらで扱うか決め、必要な変換と失敗時の処理を明示します。

開始の省略値は0です。負の値は末尾基準で開始位置を計算しますが、検索方向は前から後です。配列より大幅に前なら0から、長さ以上なら-1です。結果は開始位置からの相対番号ではなく元配列のインデックスです。重複しても開始以降の最初の一致だけを返します。

オブジェクトは同じプロパティかではなく同じ参照かで比較します。開発者ツールのConsoleで次の完全な部分コードを実行し、同じオブジェクトと新しいもの、NaN、ネイティブ代替を比較します。

const item = { id: 1 };
console.log(`same object=${$.inArray(item, [item])}`);
console.log(`new object=${$.inArray({ id: 1 }, [item])}`);
console.log(`NaN index=${$.inArray(NaN, [NaN])} | includes=${[NaN].includes(NaN)}`);
console.log(`empty=${$.inArray("a", [])}`);
const array = ["a", "b", "a"];
console.log(`start 1=${$.inArray("a", array, 1)} | start -1=${$.inArray("a", array, -1)}`);
console.log(`native index=${array.indexOf("a", 1)} | native found=${array.includes("a", 1)}`);
same object=0
new object=-1
NaN index=-1 | includes=true
empty=-1
start 1=2 | start -1=2
native index=2 | native found=true

indexOf・includesと後続処理

通常配列の位置ならネイティブのarray.indexOf(value, fromIndex)も使えます。検索一つのためにjQueryを追加する必要はありません。呼び方は$.inArray(value, array)ではなくarray.indexOf(value)です。

存在だけならarray.includes(value, fromIndex)が直接true・falseを返します。ただし比較規則は完全に同じではありません。NaNは$.inArray()とindexOf()では-1、includes()ではtrueです。オブジェクト参照と数値・文字列の区別も引き続き必要です。

検索位置で削除するなら、-1でないことを先に確認します。見つからなかった-1をそのままsplice()に渡すと末尾を消す場合があります。型、引数順、開始位置、参照、配列自体の変更を調べると原因を絞れます。