目次
概要
配列の中に「特定の値が含まれているか」を確認したり、「その値がどこにあるか」を特定したりする処理は、データ検証やUI制御において頻繁に発生します。
本記事では、インデックス番号(位置)を取得する indexOf / lastIndexOf と、存在有無を真偽値で返す includes の違いを整理し、タグ管理システムを題材に実践的な検索ロジックを実装します。
仕様(入出力)
検索メソッド比較表
| メソッド | 目的 | 戻り値 | 見つからない場合 | 特徴 |
indexOf(item, [start]) | 最初に見つかった位置を知りたい | 数値 (Index) | -1 | 先頭から検索する |
lastIndexOf(item, [start]) | 最後に見つかった位置を知りたい | 数値 (Index) | -1 | 末尾から検索する |
includes(item, [start]) | 存在するかだけ知りたい | 真偽値 (Boolean) | false | indexOf よりも直感的で高速 |
- 入力: 検索したい要素(プリミティブ値推奨)
- 出力: インデックス番号または真偽値
- 検索開始位置: 第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);
カスタムポイント
- 大文字小文字の区別:
indexOfやincludesは大文字と小文字を厳密に区別します("Js"と"js"は別物)。区別せずに検索したい場合は、一旦toLowerCase()で揃えてから比較する必要があります。 - 検索開始位置の指定:
indexOf('word', 3)のように第2引数を指定すると、インデックス3以降のみを検索対象にできます。同じ値が複数含まれている場合の「2つ目の出現位置」を探す際などに利用します。
注意点
- オブジェクトの検索はできないこれらのメソッドは「値の等価性(厳密等価
===)」で判定します。[{id:1}].includes({id:1})はfalseになります(オブジェクトの参照が異なるため)。オブジェクト配列を検索したい場合はfindIndexやsomeを使用してください。 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における配列操作の基本中の基本です。用途に合わせて正しく使い分けましょう。
