JavaScriptで配列から条件に合う要素だけを取り出したいときに使うのが、filter メソッドです。入門書では function を使った長めの書き方をよく見ますが、実際のコードでは t => t !== task のような1行の書き方が多く、初めは同じものだと気づきにくいところです。
この記事では、「タスクの一覧から1件を取り除く」処理を例に、if文を使った書き方から1行の書き方まで、4つのステップで少しずつ短くしていきます。
filterメソッドの基本
filter は、配列の要素を1つずつ関数に渡し、関数が true を返した要素だけを集めた新しい配列を作ります。false を返した要素は、新しい配列に入りません。
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
console.log(numbers); // [1, 2, 3, 4, 5](元の配列はそのまま)元の配列は変わらないのがポイントです。配列から要素を「削除する」のではなく、「残したい要素だけの配列を作り直す」と考えると分かりやすくなります。組み込みのC言語なら配列をその場で詰め直すところですが、JavaScriptでは新しい配列を作って入れ替える書き方がよく使われます。
お題:タスクの一覧から1件を取り除く
次のような、タスクを配列(this.tasks)で持つクラスを考えます。remove(task) は、引数で渡した task を一覧から取り除くメソッドです。この中身を、これから4つのステップで書き換えていきます。
class TaskList {
constructor() {
this.tasks = [];
}
add(task) {
this.tasks.push(task);
}
remove(task) {
// ここを書き換えていく
}
}ステップ1:if文で true か false を返す
まずは、処理の流れをそのまま書いた形です。
remove(task) {
this.tasks = this.tasks.filter(function (t) {
if (t === task) {
return false; // 一致したら、新しい配列に入れない
}
return true; // 一致しなければ、新しい配列に残す
});
}filter は配列の要素を1つずつ t に入れて、関数を呼びます。取り除きたい task と同じときだけ false を返すので、それ以外のタスクだけの配列ができます。その配列を this.tasks に入れ直すことで、一覧から取り除いたことになります。
ステップ2:条件式をそのまま return する
ステップ1は正しく動きますが、if文がなくても同じことが書けます。
remove(task) {
this.tasks = this.tasks.filter(function (t) {
return t !== task;
});
}t !== task(t と task が等しくない)という条件式は、それ自体が true か false になります。
- 等しくないときは
true(残す) - 等しいときは
false(入れない)
ステップ1のif文と結果が同じなので、条件式をそのまま return すれば済みます。C言語で if (a != b) return 1; else return 0; を return a != b; と書くのと同じ考え方です。
ステップ3:アロー関数にする
ES2015(ES6)で追加された「アロー関数」を使うと、function を書かずに関数を作れます。
remove(task) {
this.tasks = this.tasks.filter((t) => {
return t !== task;
});
}function (t) が (t) => に変わりました。=> が矢印(アロー)に見えるので、アロー関数と呼ばれます。この例では、動きはステップ2と同じです。アロー関数の成り立ちは「JavaScriptのアロー関数って?」で紹介しています。
ステップ4:カッコと return を省く
アロー関数には、次の2つの省略のルールがあります。
- 引数が1つだけのときは、引数を囲む
()を省ける(引数が0個や2個以上のときは省けない) - 中身が「式を返すだけ」のときは、
{}とreturnを省ける。その式の値が、そのまま戻り値になる
この2つを使うと、次のようになります。
remove(task) {
this.tasks = this.tasks.filter(t => t !== task);
}「this.tasks の中から、task と等しくないものだけを残す」という処理が、1行に収まりました。ステップ1から4は、どれも同じ動きをします。
なお、() を省くかどうかは好みやプロジェクトのルールで分かれます。コードを自動で整える Prettier は、初期設定では (t) => ... のようにカッコを付けます。
動かして確かめる
完成したクラスを使って、実際に取り除けるか確かめてみます。ブラウザのコンソールやCodePenに貼れば、そのまま動きます。
class TaskList {
constructor() {
this.tasks = [];
}
add(task) {
this.tasks.push(task);
}
remove(task) {
this.tasks = this.tasks.filter(t => t !== task);
}
}
const list = new TaskList();
const shopping = { id: 1, title: '買い物' };
const cleaning = { id: 2, title: '掃除' };
list.add(shopping);
list.add(cleaning);
list.remove(shopping);
console.log(list.tasks); // [{ id: 2, title: '掃除' }]書くときに気をつけたいこと
オブジェクトは「同じものかどうか」で比べられる
t !== task は、オブジェクトの中身ではなく、「同じオブジェクトかどうか」を比べます。中身が同じでも別々に作ったオブジェクトは、等しくありません。
const a = { id: 1, title: '買い物' };
const b = { id: 1, title: '買い物' };
console.log(a === a); // true(同じオブジェクト)
console.log(a === b); // false(中身は同じでも別のオブジェクト)C言語でいえば、構造体の中身ではなくポインタの値を比べているのに近いです。サーバーから読み直したデータなど、別のオブジェクトになっていることがあるときは、id のような値で比べます。
remove(task) {
this.tasks = this.tasks.filter(t => t.id !== task.id);
}中で this を使うときはアロー関数にする
ステップ3では「動きは同じ」と書きましたが、function とアロー関数は、関数の中の this の扱いが違います。filter に渡した関数の中で this を使うと、その違いが出ます。
class TaskList {
constructor() {
this.tasks = [];
this.selected = null; // 選んでいるタスク
}
// 動かない:function の中の this は TaskList を指さないので、エラーになる
removeSelectedNg() {
this.tasks = this.tasks.filter(function (t) {
return t !== this.selected;
});
}
// 動く:アロー関数の中の this は、外側(TaskList)の this のまま
removeSelected() {
this.tasks = this.tasks.filter(t => t !== this.selected);
}
}アロー関数は自分の this を持たず、外側の this をそのまま使います。メソッドの中で filter などを使うときは、アロー関数にしておくとこの間違いが起きません。
短くしすぎない
条件が複雑になるときは、無理に1行にせず、{} と return を使った書き方や、条件に名前を付けた変数を使うほうが読みやすくなります。短いことより、あとで読んだときに分かることを大事にしましょう。
まとめ
1行の短いコードも、分解すると次の順に省いていった結果です。
- if文で
trueかfalseを返す - 条件式をそのまま返す
- アロー関数にする
- 引数の
()と{}・returnを省く
filter は元の配列を変えず、新しい配列を作ります。オブジェクトを比べるときは「同じものかどうか」になることも覚えておきましょう。同じ考え方は、map や find などのメソッドにもそのまま使えます。
コードの中の '買い物' のような文字列の書き方は、「文字列の ‘ と ” と ` の違い」で紹介しています。



コメント