【JavaScript】配列データの検索!indexOf, lastIndexOf, includesの使い分け完全ガイド

目次

概要

配列の中に「特定の値が含まれているか」を確認したり、「その値がどこにあるか」を特定したりする処理は、データ検証やUI制御において頻繁に発生します。

本記事では、インデックス番号(位置)を取得する indexOf / lastIndexOf と、存在有無を真偽値で返す includes の違いを整理し、タグ管理システムを題材に実践的な検索ロジックを実装します。

仕様(入出力)

検索メソッド比較表

メソッド目的戻り値見つからない場合特徴
indexOf(item, [start])最初に見つかった位置を知りたい数値 (Index)-1先頭から検索する
lastIndexOf(item, [start])最後に見つかった位置を知りたい数値 (Index)-1末尾から検索する
includes(item, [start])存在するかだけ知りたい真偽値 (Boolean)falseindexOf よりも直感的で高速
  • 入力: 検索したい要素(プリミティブ値推奨)
  • 出力: インデックス番号または真偽値
  • 検索開始位置: 第2引数で指定可能(省略時は全範囲)。

基本の使い方

1. 位置を特定する (indexOf / lastIndexOf)

要素が配列の何番目にあるかを取得します。

const userIds = [101, 205, 303, 205, 401];

// 先頭から検索して最初に見つかった位置
console.log(userIds.indexOf(205));     // 1

// 末尾から検索して最初に見つかった位置(=最後に出現する位置)
console.log(userIds.lastIndexOf(205)); // 3

// 存在しない値
console.log(userIds.indexOf(999));     // -1

2. 存在確認をする (includes)

ES2016で追加されたメソッドで、if 文の条件式などで非常に便利です。

const keywords = ['Error', 'Warning', 'Info'];

if (keywords.includes('Error')) {
    console.log('エラーが含まれています');
}
// true または false が返る

コード全文(HTML / JavaScript)

ブログ投稿画面の「タグ管理機能」を想定したデモです。

新しいタグを追加する際、includes で重複を防止し、削除時には indexOf で削除対象の位置を特定します。

HTML

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tag Manager Demo</title>
    <style>
        .tag-container {
            font-family: 'Segoe UI', sans-serif;
            max-width: 400px;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 8px;
            background: #fdfdfd;
        }
        .input-area {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
        }
        input[type="text"] {
            flex: 1;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            padding: 8px 16px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover { background-color: #0056b3; }
        
        .tag-list {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            padding: 0;
            list-style: none;
        }
        .tag {
            background-color: #e9ecef;
            color: #495057;
            padding: 5px 10px;
            border-radius: 20px;
            font-size: 0.9rem;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .tag-remove {
            cursor: pointer;
            color: #999;
            font-weight: bold;
        }
        .tag-remove:hover { color: #dc3545; }
        
        .message {
            margin-top: 10px;
            font-size: 0.85rem;
            min-height: 1.2em;
        }
        .error { color: #dc3545; }
        .success { color: #28a745; }
    </style>
</head>
<body>

<div class="tag-container">
    <h3>タグ管理システム</h3>
    <div class="input-area">
        <input type="text" id="tag-input" placeholder="新しいタグを入力...">
        <button id="add-btn">追加</button>
    </div>

    <ul id="current-tags" class="tag-list">
        </ul>
    
    <div id="status-msg" class="message"></div>
</div>

<script src="tag_manager.js"></script>
</body>
</html>

JavaScript

/**
 * タグ管理スクリプト
 * 重複チェック(includes)と削除位置特定(indexOf)の実践
 */

// 現在のタグリスト
const tags = ['JavaScript', 'HTML', 'CSS'];

// DOM要素
const tagInput = document.getElementById('tag-input');
const addBtn = document.getElementById('add-btn');
const tagListElement = document.getElementById('current-tags');
const statusMsg = document.getElementById('status-msg');

/**
 * リストを描画する関数
 */
const renderTags = () => {
    tagListElement.innerHTML = '';
    
    tags.forEach((tag) => {
        const li = document.createElement('li');
        li.className = 'tag';
        li.innerHTML = `
            ${tag}
            <span class="tag-remove" data-tag="${tag}">×</span>
        `;
        tagListElement.appendChild(li);
    });

    // 削除ボタンへのイベントリスナー再設定
    document.querySelectorAll('.tag-remove').forEach(btn => {
        btn.addEventListener('click', (e) => removeTag(e.target.dataset.tag));
    });
};

/**
 * メッセージを表示する関数
 */
const showMessage = (text, type) => {
    statusMsg.textContent = text;
    statusMsg.className = `message ${type}`;
    setTimeout(() => {
        statusMsg.textContent = '';
        statusMsg.className = 'message';
    }, 2000);
};

/**
 * タグを追加する関数
 */
const addTag = () => {
    const newTag = tagInput.value.trim();
    if (!newTag) return;

    // ★ includesを使って重複チェック
    // 既に配列に含まれている場合はtrueが返る
    if (tags.includes(newTag)) {
        showMessage(`「${newTag}」は既に追加されています。`, 'error');
        return;
    }

    tags.push(newTag);
    renderTags();
    showMessage(`「${newTag}」を追加しました。`, 'success');
    tagInput.value = '';
    tagInput.focus();
};

/**
 * タグを削除する関数
 * @param {string} targetTag - 削除したいタグ名
 */
const removeTag = (targetTag) => {
    // ★ indexOfを使って配列内の位置を特定
    // 見つからない場合は -1 が返る
    const index = tags.indexOf(targetTag);

    if (index !== -1) {
        // spliceを使ってその位置の要素を削除
        tags.splice(index, 1);
        renderTags();
        showMessage(`「${targetTag}」を削除しました。`, 'success');
    }
};

// 初期化
renderTags();
addBtn.addEventListener('click', addTag);

カスタムポイント

  • 大文字小文字の区別:indexOfincludes は大文字と小文字を厳密に区別します("Js""js" は別物)。区別せずに検索したい場合は、一旦 toLowerCase() で揃えてから比較する必要があります。
  • 検索開始位置の指定:indexOf('word', 3) のように第2引数を指定すると、インデックス3以降のみを検索対象にできます。同じ値が複数含まれている場合の「2つ目の出現位置」を探す際などに利用します。

注意点

  1. オブジェクトの検索はできないこれらのメソッドは「値の等価性(厳密等価 ===)」で判定します。[{id:1}].includes({id:1})false になります(オブジェクトの参照が異なるため)。オブジェクト配列を検索したい場合は findIndexsome を使用してください。
  2. NaN の挙動の違い配列に NaN(非数)が含まれる場合、indexOf は見つけられませんが(-1になる)、includes は見つけることができます(trueになる)。
    • [NaN].indexOf(NaN) -> -1
    • [NaN].includes(NaN) -> true

応用

lastIndexOf を使った重複排除

配列の「最初の出現位置」と「最後の出現位置」が一致するかどうかを確認することで、ユニークな(重複していない)要素だけを抽出できます。

const rawData = ['A', 'B', 'A', 'C', 'B'];

const uniqueData = rawData.filter((item, index, array) => {
    // 現在のインデックスと、最初に現れるインデックスが一致するものだけ残す
    return array.indexOf(item) === index;
});

console.log(uniqueData); // ["A", "B", "C"]

まとめ

  • includes: あるかないか知りたい時(If文条件式など)。最強かつ推奨。
  • indexOf: 削除や置換のために「場所」を知りたい時。
  • lastIndexOf: 末尾から探したい、あるいは重複データの確認をしたい時。

これらはJavaScriptにおける配列操作の基本中の基本です。用途に合わせて正しく使い分けましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次