[PR] 本記事にはプロモーションが含まれています。

【JavaScript】filterメソッドの書き方|if文から1行のアロー関数まで、4ステップで短くして理解する

JavaScript

 JavaScriptで配列から条件に合う要素だけを取り出したいときに使うのが、filter メソッドです。入門書では function を使った長めの書き方をよく見ますが、実際のコードでは t => t !== task のような1行の書き方が多く、初めは同じものだと気づきにくいところです。

この記事では、「タスクの一覧から1件を取り除く」処理を例に、if文を使った書き方から1行の書き方まで、4つのステップで少しずつ短くしていきます。

filterメソッドの基本

filter は、配列の要素を1つずつ関数に渡し、関数が true を返した要素だけを集めた新しい配列を作ります。false を返した要素は、新しい配列に入りません。

JavaScript
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つのステップで書き換えていきます。

JavaScript
class TaskList {
  constructor() {
    this.tasks = [];
  }

  add(task) {
    this.tasks.push(task);
  }

  remove(task) {
    // ここを書き換えていく
  }
}

ステップ1:if文で true か false を返す

まずは、処理の流れをそのまま書いた形です。

JavaScript
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文がなくても同じことが書けます。

JavaScript
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 を書かずに関数を作れます。

JavaScript
remove(task) {
  this.tasks = this.tasks.filter((t) => {
    return t !== task;
  });
}

function (t) が (t) => に変わりました。=> が矢印(アロー)に見えるので、アロー関数と呼ばれます。この例では、動きはステップ2と同じです。アロー関数の成り立ちは「JavaScriptのアロー関数って?」で紹介しています。

ステップ4:カッコと return を省く

アロー関数には、次の2つの省略のルールがあります。

  1. 引数が1つだけのときは、引数を囲む () を省ける(引数が0個や2個以上のときは省けない)
  2. 中身が「式を返すだけ」のときは、{} と return を省ける。その式の値が、そのまま戻り値になる

この2つを使うと、次のようになります。

JavaScript
remove(task) {
  this.tasks = this.tasks.filter(t => t !== task);
}

「this.tasks の中から、task と等しくないものだけを残す」という処理が、1行に収まりました。ステップ1から4は、どれも同じ動きをします。

なお、() を省くかどうかは好みやプロジェクトのルールで分かれます。コードを自動で整える Prettier は、初期設定では (t) => ... のようにカッコを付けます。

動かして確かめる

完成したクラスを使って、実際に取り除けるか確かめてみます。ブラウザのコンソールやCodePenに貼れば、そのまま動きます。

JavaScript
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 は、オブジェクトの中身ではなく、「同じオブジェクトかどうか」を比べます。中身が同じでも別々に作ったオブジェクトは、等しくありません。

JavaScript
const a = { id: 1, title: '買い物' };
const b = { id: 1, title: '買い物' };

console.log(a === a); // true(同じオブジェクト)
console.log(a === b); // false(中身は同じでも別のオブジェクト)

C言語でいえば、構造体の中身ではなくポインタの値を比べているのに近いです。サーバーから読み直したデータなど、別のオブジェクトになっていることがあるときは、id のような値で比べます。

JavaScript
remove(task) {
  this.tasks = this.tasks.filter(t => t.id !== task.id);
}

中で this を使うときはアロー関数にする

ステップ3では「動きは同じ」と書きましたが、function とアロー関数は、関数の中の this の扱いが違います。filter に渡した関数の中で this を使うと、その違いが出ます。

JavaScript
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行の短いコードも、分解すると次の順に省いていった結果です。

  1. if文で true か false を返す
  2. 条件式をそのまま返す
  3. アロー関数にする
  4. 引数の () と {}・return を省く

filter は元の配列を変えず、新しい配列を作ります。オブジェクトを比べるときは「同じものかどうか」になることも覚えておきましょう。同じ考え方は、map や find などのメソッドにもそのまま使えます。

コードの中の '買い物' のような文字列の書き方は、「文字列の ‘ と ” と ` の違い」で紹介しています。

ほかの開発・プログラミングの記事は、開発・プログラミングメモの目次から、テーマ別に探せます。

このブログは、にほんブログ村と人気ブログランキングに参加しています。記事が役に立ったら、下のランキングのバナーのどれかを押して応援してもらえると励みになります。

ブログランキング・にほんブログ村へにほんブログ村

サーバー構築・運用ランキング プログラムランキング

JavaScript
watasoftdevをフォローする

コメント