【初心者向け】JavaScript文字列検索 - indexOf/includesの使い分け

JavaScript文字列検索メソッドindexOfとincludesの基本的な使い方から使い分けまで詳しく解説。戻り値の違い、パフォーマンス、実践的な使用例を初心者向けに分かりやすく説明します。

Learning Next 運営
36 分で読めます

【初心者向け】JavaScript文字列検索 - indexOf/includesの使い分け

「JavaScriptで文字列の中に特定の文字が含まれているかチェックしたい」と思ったことはありませんか?

「ユーザーの入力に特定のキーワードが含まれているか確認したい」 「文字列の位置を知りたい」 「どちらのメソッドを使えばいいの?」

そんな疑問を抱いている方は多いと思います。 でも大丈夫です!

この記事では、JavaScriptの文字列検索メソッドindexOf()とincludes()の基本から実践的な使い分けまでを詳しく解説します。 戻り値の違い、パフォーマンス、実践的な使用例を実際のコード例とともに初心者向けに分かりやすく説明していきます。

きっと「文字列検索ってこんなに便利だったんだ!」と感じられるはずですよ。

indexOf()とincludes()の基本的な違い

indexOf()メソッドの特徴

indexOf()は、指定した文字列が最初に見つかった位置のインデックスを返します。 見つからない場合は-1を返します。

let text = "Hello, World! Hello, JavaScript!";
// "Hello"を検索
console.log(text.indexOf("Hello")); // 0(最初の位置)
console.log(text.indexOf("World")); // 7
console.log(text.indexOf("JavaScript")); // 21
console.log(text.indexOf("Python")); // -1(見つからない)
// 大文字小文字を区別
console.log(text.indexOf("hello")); // -1(見つからない)
console.log(text.indexOf("HELLO")); // -1(見つからない)

indexOf("Hello")は0を返します。 これは「Hello」が文字列の先頭(0番目)にあるからです。

includes()メソッドの特徴

includes()は、指定した文字列が含まれているかどうかをtrueまたはfalseで返します

let text = "Hello, World! Hello, JavaScript!";
// "Hello"を検索
console.log(text.includes("Hello")); // true
console.log(text.includes("World")); // true
console.log(text.includes("JavaScript")); // true
console.log(text.includes("Python")); // false
// 大文字小文字を区別
console.log(text.includes("hello")); // false
console.log(text.includes("HELLO")); // false

includes()は単純にtrue/falseで答えてくれるので、存在確認にとても便利です。

戻り値の比較を理解しよう

let sentence = "JavaScriptは素晴らしいプログラミング言語です";
// indexOf()の戻り値
let indexResult = sentence.indexOf("JavaScript");
console.log("indexOf結果:", indexResult); // 0
console.log("型:", typeof indexResult); // "number"
// includes()の戻り値
let includesResult = sentence.includes("JavaScript");
console.log("includes結果:", includesResult); // true
console.log("型:", typeof includesResult); // "boolean"
// 見つからない場合
console.log("indexOf (見つからない):", sentence.indexOf("Python")); // -1
console.log("includes (見つからない):", sentence.includes("Python")); // false

indexOf()は数値、includes()は真偽値を返すことが大きな違いです。

indexOf()の詳細と使い方をマスター

基本的な書き方を覚えよう

// 基本構文
// string.indexOf(searchValue, startIndex)
let text = "今日は良い天気です。今日は散歩日和です。";
// 基本的な検索
console.log(text.indexOf("今日")); // 0
console.log(text.indexOf("天気")); // 4
console.log(text.indexOf("雨")); // -1

indexOfの第2引数で検索開始位置を指定できます。

// 開始位置を指定
console.log(text.indexOf("今日", 0)); // 0(最初の"今日")
console.log(text.indexOf("今日", 1)); // 10(2番目の"今日")
console.log(text.indexOf("今日", 15)); // -1(15番目以降には"今日"がない)

複数回の検索をしてみよう

文字列内のすべての出現位置を見つける関数を作ってみましょう。

// 文字列内のすべての出現位置を見つける
function findAllIndexes(text, searchValue) {
let indexes = [];
let index = text.indexOf(searchValue);
while (index !== -1) {
indexes.push(index);
index = text.indexOf(searchValue, index + 1);
}
return indexes;
}
let text = "JavaScriptでWebアプリを作る。JavaScriptは楽しい。JavaScriptを学ぼう。";
let allIndexes = findAllIndexes(text, "JavaScript");
console.log("JavaScriptの出現位置:", allIndexes); // [0, 18, 31]

この関数では、whileループを使って文字列内のすべての「JavaScript」を見つけています。 見つかった位置を配列に保存して、最後に返しています。

実用的な使用例を見てみよう

ファイル拡張子を取得する関数を作ってみましょう。

// ファイル拡張子の取得
function getFileExtension(filename) {
let lastDotIndex = filename.lastIndexOf(".");
if (lastDotIndex === -1) {
return null; // 拡張子なし
}
return filename.substring(lastDotIndex + 1).toLowerCase();
}
console.log(getFileExtension("document.pdf")); // "pdf"
console.log(getFileExtension("image.jpeg")); // "jpeg"
console.log(getFileExtension("script.min.js")); // "js"
console.log(getFileExtension("README")); // null

lastIndexOfを使うことで、最後の「.」の位置を見つけています。 これにより「script.min.js」のような複数のドットがあるファイル名でも正しく拡張子を取得できます。

メールアドレスの基本的な検証も作れます。

// メールアドレスの基本的な検証
function isValidEmail(email) {
let atIndex = email.indexOf("@");
let dotIndex = email.lastIndexOf(".");
// @マークが存在し、.が@より後にある
if (atIndex > 0 && dotIndex > atIndex + 1 && dotIndex < email.length - 1) {
return true;
}
return false;
}
console.log(isValidEmail("user@example.com")); // true
console.log(isValidEmail("user.example.com")); // false
console.log(isValidEmail("@example.com")); // false
console.log(isValidEmail("user@")); // false

簡単なチェックですが、@マークと.の位置関係を確認することで基本的な検証ができます。

includes()の詳細と使い方をマスター

基本的な書き方を覚えよう

// 基本構文
// string.includes(searchValue, startIndex)
let text = "プログラミングは創造的な活動です";
// 基本的な検索
console.log(text.includes("プログラミング")); // true
console.log(text.includes("創造的")); // true
console.log(text.includes("退屈")); // false

includes()も第2引数で検索開始位置を指定できます。

// 開始位置を指定
console.log(text.includes("プログラミング", 0)); // true
console.log(text.includes("プログラミング", 5)); // false
console.log(text.includes("創造的", 8)); // true

条件分岐での使用例

ユーザー入力の検証機能を作ってみましょう。

// ユーザー入力の検証
function validateUserInput(input) {
let errors = [];
// 禁止ワードチェック
let forbiddenWords = ["spam", "advertisement", "promotion"];
forbiddenWords.forEach(word => {
if (input.toLowerCase().includes(word.toLowerCase())) {
errors.push(`禁止ワード "${word}" が含まれています`);
}
});
// 必須キーワードチェック
let requiredWords = ["JavaScript", "プログラミング"];
requiredWords.forEach(word => {
if (!input.includes(word)) {
errors.push(`必須キーワード "${word}" が含まれていません`);
}
});
return {
isValid: errors.length === 0,
errors: errors
};
}
let userInput1 = "JavaScriptプログラミングについて学びます";
let userInput2 = "Pythonについて学びたいです";
console.log("入力1の検証:", validateUserInput(userInput1));
console.log("入力2の検証:", validateUserInput(userInput2));

この関数では、禁止ワードと必須キーワードをチェックして、検証結果を返します。 includes()を使うことで、簡潔で読みやすいコードになります。

フィルタリング機能を実装

商品検索機能を作ってみましょう。

// 商品検索機能
let products = [
{ name: "ノートパソコン", category: "electronics", description: "高性能なプログラミング向けノートPC" },
{ name: "プログラミング入門書", category: "books", description: "JavaScript初心者向けの書籍" },
{ name: "ワイヤレスマウス", category: "electronics", description: "プログラマー向けの高精度マウス" },
{ name: "コーヒーマグ", category: "lifestyle", description: "プログラミング中に使えるマグカップ" },
{ name: "Web開発ガイド", category: "books", description: "HTML、CSS、JavaScriptの総合ガイド" }
];
// キーワードで商品を検索
function searchProducts(keyword) {
return products.filter(product => {
return product.name.includes(keyword) ||
product.description.includes(keyword);
});
}
// カテゴリで商品を検索
function searchByCategory(category) {
return products.filter(product => product.category.includes(category));
}
console.log("プログラミング検索:", searchProducts("プログラミング"));
console.log("electronics検索:", searchByCategory("electronics"));

filterメソッドとincludesを組み合わせることで、効率的な検索機能を実装できます。

複数条件での検索も作れます。

// 複数条件で検索
function advancedSearch(keywords, category = null) {
return products.filter(product => {
// カテゴリチェック
if (category && !product.category.includes(category)) {
return false;
}
// キーワードチェック(すべてのキーワードが含まれている必要がある)
return keywords.every(keyword =>
product.name.includes(keyword) ||
product.description.includes(keyword)
);
});
}
console.log("JavaScript + books検索:", advancedSearch(["JavaScript"], "books"));

everyメソッドを使って、すべてのキーワードが含まれているかチェックしています。

大文字小文字を無視した検索

基本的な対処法を覚えよう

JavaScriptの文字列検索は大文字小文字を区別します。 これを無視するには、事前に文字列を統一する必要があります。

// 大文字小文字を無視したindexOf
function indexOfIgnoreCase(text, searchValue) {
return text.toLowerCase().indexOf(searchValue.toLowerCase());
}
// 大文字小文字を無視したincludes
function includesIgnoreCase(text, searchValue) {
return text.toLowerCase().includes(searchValue.toLowerCase());
}
let text = "JavaScript Programming Tutorial";
// 通常の検索(大文字小文字を区別)
console.log(text.indexOf("javascript")); // -1
console.log(text.includes("PROGRAMMING")); // false
// 大文字小文字を無視した検索
console.log(indexOfIgnoreCase(text, "javascript")); // 0
console.log(includesIgnoreCase(text, "PROGRAMMING")); // true

toLowerCase()を使って両方の文字列を小文字に変換してから比較します。

より実用的な検索クラスを作ろう

// より実用的な検索関数
class TextSearcher {
constructor(caseSensitive = true) {
this.caseSensitive = caseSensitive;
}
normalize(text) {
return this.caseSensitive ? text : text.toLowerCase();
}
indexOf(text, searchValue, startIndex = 0) {
return this.normalize(text).indexOf(this.normalize(searchValue), startIndex);
}
includes(text, searchValue, startIndex = 0) {
if (startIndex > 0) {
text = text.substring(startIndex);
}
return this.normalize(text).includes(this.normalize(searchValue));
}
findAll(text, searchValue) {
let indexes = [];
let index = this.indexOf(text, searchValue);
while (index !== -1) {
indexes.push(index);
index = this.indexOf(text, searchValue, index + 1);
}
return indexes;
}
}
// 使用例
let searcher = new TextSearcher(false); // 大文字小文字を無視
let document = "JavaScript is Great! javascript is powerful. JAVASCRIPT rocks!";
console.log("JavaScript出現位置:", searcher.findAll(document, "javascript"));
// [0, 20, 42]

このクラスを使うことで、大文字小文字の扱いを統一して管理できます。

日本語での検索の注意点

// 日本語の検索での注意点
function japaneseSearch() {
let text = "こんにちは、世界!今日は良い天気ですね。";
// ひらがな・カタカナ・漢字の検索
console.log("ひらがな検索:", text.includes("こんにちは")); // true
console.log("漢字検索:", text.includes("世界")); // true
console.log("部分検索:", text.includes("良い天気")); // true
// 半角・全角の違い
let textWithNumbers = "2024年の目標は、プログラミングスキルの向上です。";
console.log("全角数字:", textWithNumbers.includes("2024")); // true
// 濁点・半濁点の扱い
let hiraganaText = "がんばって、プログラミングを学びましょう。";
console.log("濁点検索:", hiraganaText.includes("がんばって")); // true
console.log("濁点なし:", hiraganaText.includes("かんはって")); // false
}
japaneseSearch();

日本語では濁点・半濁点や全角・半角の違いにも注意が必要です。

実践的な使用例を学ぼう

ログ解析システムを作ってみよう

// ログ解析クラス
class LogAnalyzer {
constructor() {
this.errorKeywords = ["ERROR", "FATAL", "EXCEPTION"];
this.warningKeywords = ["WARN", "WARNING"];
this.infoKeywords = ["INFO", "DEBUG"];
}
// ログレベルを判定
getLogLevel(logLine) {
if (this.errorKeywords.some(keyword => logLine.includes(keyword))) {
return "ERROR";
} else if (this.warningKeywords.some(keyword => logLine.includes(keyword))) {
return "WARNING";
} else if (this.infoKeywords.some(keyword => logLine.includes(keyword))) {
return "INFO";
}
return "UNKNOWN";
}
// エラーメッセージを抽出
extractErrorMessage(logLine) {
let errorIndex = logLine.indexOf("ERROR");
if (errorIndex !== -1) {
return logLine.substring(errorIndex);
}
return null;
}
// ログを分析
analyzeLog(logLines) {
let analysis = {
total: logLines.length,
error: 0,
warning: 0,
info: 0,
unknown: 0,
errorMessages: []
};
logLines.forEach(line => {
let level = this.getLogLevel(line);
analysis[level.toLowerCase()]++;
if (level === "ERROR") {
let errorMsg = this.extractErrorMessage(line);
if (errorMsg) {
analysis.errorMessages.push(errorMsg);
}
}
});
return analysis;
}
}
// 使用例
let logAnalyzer = new LogAnalyzer();
let sampleLogs = [
"2024-01-01 10:00:00 INFO アプリケーション開始",
"2024-01-01 10:01:00 ERROR データベース接続エラー",
"2024-01-01 10:02:00 WARNING メモリ使用量が高い",
"2024-01-01 10:03:00 INFO ユーザーログイン成功",
"2024-01-01 10:04:00 FATAL システムクラッシュ"
];
let analysis = logAnalyzer.analyzeLog(sampleLogs);
console.log("ログ分析結果:", analysis);

includesindexOfを組み合わせることで、実用的なログ解析システムを構築できます。

テキストハイライト機能を実装

// テキストハイライト機能
class TextHighlighter {
constructor(options = {}) {
this.caseSensitive = options.caseSensitive || false;
this.highlightClass = options.highlightClass || 'highlight';
this.maxResults = options.maxResults || 100;
}
// テキスト内の検索語をハイライト
highlight(text, searchTerms) {
if (!Array.isArray(searchTerms)) {
searchTerms = [searchTerms];
}
let highlightedText = text;
let matchInfo = {
totalMatches: 0,
matchesByTerm: {}
};
searchTerms.forEach((term, index) => {
let matches = this.findMatches(highlightedText, term);
matchInfo.matchesByTerm[term] = matches.length;
matchInfo.totalMatches += matches.length;
if (matches.length > 0) {
highlightedText = this.replaceMatches(
highlightedText,
term,
`<span class="${this.highlightClass} highlight-${index}">${term}</span>`
);
}
});
return {
highlightedText: highlightedText,
matchInfo: matchInfo
};
}
// マッチ箇所を検索
findMatches(text, searchTerm) {
let matches = [];
let searchText = this.caseSensitive ? text : text.toLowerCase();
let searchValue = this.caseSensitive ? searchTerm : searchTerm.toLowerCase();
let index = 0;
while (index < searchText.length && matches.length < this.maxResults) {
let foundIndex = searchText.indexOf(searchValue, index);
if (foundIndex === -1) break;
matches.push({
index: foundIndex,
term: text.substring(foundIndex, foundIndex + searchTerm.length)
});
index = foundIndex + 1;
}
return matches;
}
}
// 使用例
let highlighter = new TextHighlighter({
caseSensitive: false,
highlightClass: 'search-highlight'
});
let article = `
JavaScriptは非常に人気のあるプログラミング言語です。
JavaScriptを学ぶことで、Webアプリケーションの開発ができるようになります。
また、JavaScriptはサーバーサイドの開発にも使用できます。
`;
let searchResult = highlighter.highlight(article, ["JavaScript", "開発"]);
console.log("ハイライト結果:", searchResult);

この機能により、検索結果をビジュアルに表示できます。

よくある間違いとその対策

indexOf()の戻り値の扱い

よくある間違いとして、indexOf()の戻り値を真偽値として扱ってしまうことがあります。

// 間違った例
function badIndexOfUsage(text, searchValue) {
if (text.indexOf(searchValue)) { // 間違い!0の場合falseになる
console.log("見つかりました");
} else {
console.log("見つかりませんでした");
}
}
// 正しい例
function correctIndexOfUsage(text, searchValue) {
if (text.indexOf(searchValue) !== -1) { // 正しい!
console.log("見つかりました");
} else {
console.log("見つかりませんでした");
}
}
// より良い例(includes使用)
function betterUsage(text, searchValue) {
if (text.includes(searchValue)) { // 最も読みやすい
console.log("見つかりました");
} else {
console.log("見つかりませんでした");
}
}
// テスト
let testText = "Hello, World!";
badIndexOfUsage(testText, "Hello"); // 0が返されるため"見つかりませんでした"
correctIndexOfUsage(testText, "Hello"); // 正しく"見つかりました"
betterUsage(testText, "Hello"); // 正しく"見つかりました"

indexOf()の戻り値は必ず-1と比較することが重要です。

大文字小文字の考慮不足

大文字小文字を適切に処理しない問題もよくあります。

// 問題のある例
function caseProblematicSearch(userInput, keywords) {
return keywords.filter(keyword => userInput.includes(keyword));
}
// 改善された例
function caseInsensitiveSearch(userInput, keywords) {
let normalizedInput = userInput.toLowerCase();
return keywords.filter(keyword =>
normalizedInput.includes(keyword.toLowerCase())
);
}
// テスト例
let userQuery = "JavaScript Programming";
let searchKeywords = ["javascript", "PROGRAMMING", "Python", "Java"];
console.log("問題例:", caseProblematicSearch(userQuery, searchKeywords));
console.log("改善例:", caseInsensitiveSearch(userQuery, searchKeywords));

事前に文字列を正規化することで、この問題を解決できます。

パフォーマンスの問題

大量のデータを扱う時は、パフォーマンスにも注意が必要です。

// 非効率な例
function inefficientMultipleSearch(text, searchValues) {
let results = [];
// 毎回文字列全体を検索(非効率)
searchValues.forEach(value => {
if (text.includes(value)) {
results.push(value);
}
});
return results;
}
// 効率化された例
function efficientMultipleSearch(text, searchValues) {
let lowerText = text.toLowerCase();
// 一度だけ小文字化して再利用
return searchValues.filter(value =>
lowerText.includes(value.toLowerCase())
);
}

一度だけ前処理を行うことで、パフォーマンスを大幅に改善できます。

まとめ

JavaScript文字列検索メソッドindexOf()とincludes()について詳しく解説しました。

基本的な違いをおさらい

  • indexOf(): 位置を数値で返す(見つからない場合は-1)
  • includes(): 存在をbooleanで返す(true/false)

それぞれの特徴を理解することが重要です。

使い分けの指針を覚えよう

適切な場面で使い分けることで、コードがより読みやすくなります。

  • 位置が必要: indexOf()やlastIndexOf()を使用
  • 存在確認のみ: includes()を使用
  • 大文字小文字を無視: 事前に正規化処理
  • パフォーマンス重視: 用途に応じた最適化

実践的な活用法を身につけよう

  • ログ解析: エラーレベルの判定
  • 検索機能: フィルタリングとハイライト
  • 入力補完: スコアリング付きサジェスト
  • バリデーション: 禁止ワードチェック

これらの活用例を参考に、実際のプロジェクトで応用してみてください。

注意すべきポイントを確認

  • indexOf()の戻り値!== -1で判定
  • 大文字小文字の適切な処理
  • パフォーマンスを考慮した実装
  • エラーハンドリングの実装

適切な文字列検索メソッドを選択し活用することで、より効率的で保守しやすいJavaScriptアプリケーションが作れるようになります。

まずは基本的な使い方から始めて、徐々に高度なテクニックも取り入れてみてください。 ぜひ今日から、これらの知識を活用してより良い文字列検索機能を実装してみませんか?

関連記事