JavaScriptでArray.prototype.filter()を使いこなす5つの方法

JavaScriptにおけるArray.prototype.filter()の使い方JS
この記事は約15分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●Array.prototype.filter()とは?

JavaScriptを使う上で、配列の操作は欠かせない要素ですよね。

その中でも、Array.prototype.filter()は非常に便利なメソッドの1つです。

filter()は、配列の各要素に対して指定されたテスト関数を実行し、その結果が真となる要素だけを抽出した新しい配列を生成します。

つまり、特定の条件を満たす要素だけを取り出したい時に、filter()は強力な武器になるんです。

初心者の方にとっては、filter()の使い方がいまいちピンとこないかもしれません。

でも大丈夫、これからサンプルコードを交えながら、filter()の基本的な使い方から応用例まで、わかりやすく解説していきますから。

一緒にfilter()を使いこなせるようになりましょう!

○filter()の基本的な使い方

filter()の基本的な使い方は、次のような感じです。

const 元の配列 = [要素1, 要素2, 要素3, ...];
const 新しい配列 = 元の配列.filter(テスト関数);

ここで、テスト関数は各要素に対して実行される関数で、真偽値を返します。

テスト関数が真を返した要素だけが、新しい配列に含まれることになります。

○サンプルコード1:条件を満たす要素のみ抽出

具体的なサンプルコードを見てみましょう。

次のコードでは、数値の配列から偶数だけを抽出しています。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const evenNumbers = numbers.filter(function(num) {
  return num % 2 === 0;
});

console.log(evenNumbers);

実行結果

[2, 4, 6, 8, 10]

ここでのテスト関数は、num % 2 === 0となっています。

これは、各要素であるnumを2で割った余りが0かどうかを判定しているんですね。

余りが0となる偶数だけが、新しい配列evenNumbersに含まれることになります。

○サンプルコード2:複数の条件を組み合わせる

filter()では、複数の条件を組み合わせることもできます。

次のコードでは、数値の配列から3の倍数かつ10以下の数値だけを抽出しています。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

const filteredNumbers = numbers.filter(function(num) {
  return num % 3 === 0 && num <= 10;
});

console.log(filteredNumbers);

実行結果

[3, 6, 9]

ここでのテスト関数は、num % 3 === 0 && num <= 10となっています。

各要素が3の倍数かつ10以下の数値であるかどうかを判定しているわけですね。

論理演算子の&&を使うことで、複数の条件を組み合わせることができるんです。

●filter()とarrow functionの組み合わせ

さて、先ほどまではfilter()の基本的な使い方について見てきましたが、実はもっと便利な書き方があるんです。

それが、arrow functionを使った記述方法です。

arrow functionを使うと、コードがよりシンプルで読みやすくなるんですよ。

arrow functionってなんだか難しそうだと思うかもしれませんが、慣れてしまえば簡単ですから、一緒にチャレンジしてみましょう!

○サンプルコード3:arrow functionを使った記述

先ほどのサンプルコード1を、arrow functionを使って書き換えてみるとこんな感じになります。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers);

実行結果

[2, 4, 6, 8, 10]

arrow functionを使うことで、function()の記述が省略できているのがわかりますね。

num => の部分がarrow functionの記述で、numは配列の各要素を表します。

そして、=> の右側に条件式を書くだけで、その条件を満たす要素だけが抽出されるんです。

正直なところ、最初は少しややこしく感じるかもしれません。

でも、arrow functionを使うことでコードがスッキリと短くなるので、ぜひ積極的に使っていきたいところです。

慣れていけば、きっとその便利さが実感できるはずですよ。

○サンプルコード4:オブジェクトの配列をフィルタリング

次は、オブジェクトの配列をfilter()でフィルタリングする例を見てみましょう。

次のコードでは、年齢が20歳以上の人だけを抽出しています。

const people = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Charlie", age: 21 },
  { name: "David", age: 19 }
];

const adults = people.filter(person => person.age >= 20);

console.log(adults);

実行結果

[
  { name: "Alice", age: 25 },
  { name: "Charlie", age: 21 }
]

ここでは、オブジェクトの配列peopleに対してfilter()を使っています。

arrow functionの中では、各要素であるpersonのage属性が20以上であるかどうかを判定しています。

条件を満たすオブジェクトだけが、新しい配列adultsに含まれることになります。

●filter()とmap()の組み合わせ

ここまでfilter()の基本的な使い方や、arrow functionとの組み合わせについて見てきましたが、実はfilter()は他のメソッドと組み合わせることで、さらに強力なツールになるんです。

特に、map()メソッドとの組み合わせは非常に便利で、配列の要素をフィルタリングしつつ、同時に要素を変換するなんてこともできちゃいます。

一体どんなことができるのか、ワクワクしますよね。

では早速、filter()とmap()を組み合わせた例を見ていきましょう!

○サンプルコード5:filter()とmap()を組み合わせた例

先ほどのサンプルコード4で使ったpeopleの配列を使って、filter()とmap()を組み合わせてみます。

今度は、20歳以上の人の名前だけを抽出してみましょう。

const people = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Charlie", age: 21 },
  { name: "David", age: 19 }
];

const adultNames = people
  .filter(person => person.age >= 20)
  .map(person => person.name);

console.log(adultNames);

実行結果

["Alice", "Charlie"]

このコードでは、まずfilter()で20歳以上の人だけを抽出し、その結果に対してmap()を適用することで、各要素のname属性だけを取り出しています。

filter()とmap()をドット(.)でつなぐことで、このようにメソッドを連結して使うことができるんです。

●よくあるエラーと対処法

Array.prototype.filter()を使っていると、時々エラーに遭遇することがあるんですよね。

特に初心者の頃は、エラーメッセージを見ても何が問題なのかさっぱりわからなくて、頭を抱えたものです。

ただ、よくあるエラーとその対処法を知っておけば、問題解決の糸口が見えてくるはずです。

ここでは、私がこれまでに遭遇したことのある代表的なエラーを3つ紹介します。

きっとみなさんも同じようなエラーに悩まされたことがあるのではないでしょうか。

解決策を探っていきましょう!

○TypeError: Cannot read property ‘filter’ of undefined

まず最初に紹介するのは、”TypeError: Cannot read property ‘filter’ of undefined”というエラーです。

これは、filter()を呼び出そうとしている対象が、undefinedだったために発生するエラーなんですね。

例えば、こんなコードを実行したとします。

let numbers;
const evenNumbers = numbers.filter(num => num % 2 === 0);

このコードでは、numbersが未定義(undefined)の状態でfilter()を呼び出そうとしているため、エラーが発生してしまいます。

対処法としては、filter()を呼び出す前に、対象の配列が確実に定義されているかどうかを確認することが大切です。

例えば、次のようにif文を使って確認するのが一般的ですね。

let numbers;
if (Array.isArray(numbers)) {
  const evenNumbers = numbers.filter(num => num % 2 === 0);
  console.log(evenNumbers);
} else {
  console.log("numbersは配列ではありません");
}

Array.isArray()を使うことで、numbersが配列であるかどうかを判定しています。

配列であれば、filter()を実行し、そうでない場合はエラーメッセージを表示するようにしていますね。

○filtered変数が空の配列になってしまう

次に紹介するのは、filter()を実行した結果、期待していた要素が得られずに、空の配列になってしまうケースです。

これは、filter()の条件設定に問題があることが多いんですよ。

例えば、次のようなコードがあったとします。

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2);
console.log(evenNumbers);

実行結果

[1, 3, 5]

ここでは、偶数を抽出したつもりが、奇数が得られてしまっています。

これは、filter()の条件式がnum % 2となっており、0以外の値(つまり奇数)がtrueとして評価されるためです。

対処法としては、条件式を正しく設定することが重要ですね。

先ほどのコードを次のように修正すれば、期待通りの結果が得られます。

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers);

実行結果

[2, 4]

条件式をnum % 2 === 0とすることで、偶数だけが抽出されるようになりました。

filter()を使う際は、条件設定に十分気をつけましょう。

○パフォーマンス面での注意点

最後に、パフォーマンス面での注意点について触れておきます。

filter()は配列の各要素に対して条件チェックを行うため、配列の要素数が多いとパフォーマンスが低下する可能性があるんです。

例えば、次のようなコードを考えてみましょう。

const largeNumbers = Array(1000000).fill().map((_, i) => i);
console.time("filter");
const evenNumbers = largeNumbers.filter(num => num % 2 === 0);
console.timeEnd("filter");

実行結果

filter: 61.43ms

このコードでは、100万個の要素を持つ配列に対してfilter()を実行しています。

実行時間を計測してみると、60ミリ秒以上かかっていることがわかります。

パフォーマンスが重要な場面では、filter()の使用を避けるか、配列のサイズを小さくするなどの工夫が必要になります。

また、filter()の条件をできるだけシンプルにすることも大切です。

●filter()の応用例

さて、ここまでfilter()の基本的な使い方やよくあるエラーについて見てきましたが、実際の開発では、もっと複雑な配列操作が必要になることも多いですよね。

ただ、filter()を応用することで、そんな複雑な処理もスマートに書けるようになります。

ここからは、filter()の応用例を4つ紹介していきます。実務でも役立つテクニックばかりですから、ぜひ覚えておいてくださいね。

一緒に、filter()の可能性を探っていきましょう!

○サンプルコード6:重複する要素の削除

まず最初に紹介するのは、配列から重複する要素を削除する方法です。

これは、ユニークな値だけを取り出したい場面で役立ちます。

次のコードでは、filter()とindexOf()を組み合わせることで、重複する要素を取り除いています。

const numbers = [1, 2, 3, 2, 4, 3, 5];
const uniqueNumbers = numbers.filter((num, index, self) => {
  return self.indexOf(num) === index;
});
console.log(uniqueNumbers);

実行結果

[1, 2, 3, 4, 5]

ここでのポイントは、filter()のコールバック関数の引数です。

numは各要素の値、indexはその要素のインデックス、selfは配列自身を表します。

コールバック関数の中では、indexOf()を使って、その要素が配列の中で最初に登場する位置を取得し、それがその要素自身のインデックスと一致するかどうかを判定しています。

これによって、重複する要素が除外されるわけですね。

○サンプルコード7:配列の要素を条件に応じて分割

次に紹介するのは、配列の要素を条件に応じて分割する方法です。

例えば、偶数と奇数に分けたい場合などに使えます。

次のコードでは、filter()を2回使うことで、元の配列を偶数と奇数の2つの配列に分割しています。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(num => num % 2 === 0);
const oddNumbers = numbers.filter(num => num % 2 !== 0);
console.log(evenNumbers);
console.log(oddNumbers);

実行結果

[2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]

1回目のfilter()では偶数だけを、2回目のfilter()では奇数だけを抽出していますね。

条件を変えれば、他の基準で配列を分割することも可能です。

○サンプルコード8:オブジェクトの配列から特定のプロパティを抽出

3つ目の応用例は、オブジェクトの配列から特定のプロパティを抽出する方法です。

APIから取得したデータを加工する際などに重宝しますよ。

次のコードでは、人物の配列から、成人(20歳以上)の名前だけを抽出しています。

const people = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Charlie", age: 21 },
  { name: "David", age: 19 }
];
const adultNames = people
  .filter(person => person.age >= 20)
  .map(person => person.name);
console.log(adultNames);

実行結果

["Alice", "Charlie"]

ここでは、filter()とmap()を組み合わせることで、条件に合致する要素のプロパティを抽出しています。

先ほど紹介したテクニックの復習にもなりますね。

○サンプルコード9:無限スクロールの実装

最後に、少し高度な応用例として、無限スクロールの実装について触れておきます。

無限スクロールとは、ページの下部までスクロールすると、自動的に次のコンテンツが読み込まれる機能のことですね。

次のコードは、無限スクロールの実装の一部ですが、filter()を使って、まだ表示していないアイテムだけを取得しています。

const allItems = [/* 大量のアイテムデータ */];
const displayedItems = [/* 表示済みのアイテムデータ */];
const nextItems = allItems.filter(item => !displayedItems.includes(item));
// 次のアイテムを表示する処理

allItemsは全アイテムのデータ、displayedItemsは表示済みのアイテムのデータを表します。

filter()の中では、allItemsの各要素が、displayedItemsに含まれていないかどうかを判定しています。

これにより、まだ表示していないアイテムだけを取得できるわけですね。

まとめ

今回は、Array.prototype.filter()について、基本的な使い方から応用例まで詳しく見てきました。

filter()を使いこなすことで、配列操作の幅が大きく広がることを実感していただけたのではないでしょうか。

ここで学んだことを活かして、ぜひ実際の開発で試してみてください。

エラーにぶつかることもあるかもしれませんが、諦めずに向き合うことが大切です。

みなさんのJavaScriptの旅が、より実りあるものになりますように。