現役エンジニアが教える!実践JavaScript入門 〜リスト処理編〜

公開日:2021-07-21
JavaScript
リスト処理

https://youtu.be/Xt6sDxxB-5g

今回扱う内容:リスト処理

JavaScriptらしいリスト処理を理解しよう。

JavaScript らしいリスト処理とは

JavaScriptらしいリスト処理とは「関数型プログラミング」的なリスト処理のことです。具体的には次の3つのステップを意識して実装していくことになります。

  1. for文を使わない
    1つ目はfor文を使わないで記述することです。JavaScriptではリスト処理用の関数を使うことで、for文を使わずに配列に対して何かしらの処理を行うことができます。実際の開発の現場でもfor文を書くことはあまりありません。

  2. forEachをあまり使わない
    次にforEachをあまり使わないで記述することです。目的に応じてforEachよりも更に適した処理、関数があるのでそちらをおすすめします。ですが新しい値を生成する必要がない処理の場合には、for文の代わりにforEachを使うことはあります。

  3. filter、map、reduceを組み合わせて使う
    最後はfor文とforEachの代わりにfilter、map、reduceを組み合わせて処理を記述することです。これらの関数が利用される理由としては、元のリストを変更しない(非破壊)で新しいリストを生成する、という点があります。元のリスト(配列)を変更してしまうことは、意図せぬバグが起こしてしまう場合があります。filter、map、reduceといった関数は、何らかの処理の後に新しいリストを生成して別の変数に代入することによって元のリストを変更せず処理を実行できます。こちらの3つの関数を組み合わせて使うことで色々なリスト処理をJavaScriptらしく書くことができます。

filter、map、reduce の使い方

filter、map、reduce関数のそれぞれの使い方は次のようになっています。

filter()メソッド

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filterhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

与えられた関数によって実装されたテストに合格した全ての配列からなる新しい配列を生成します。つまり配列の「検索」に使う関数です。何らかの条件を関数で渡すことで、それに合致する配列の要素のみをフィルタリングし、新しい配列として生成します。これを変数に代入することでfilterされた新しい配列を取得できます。

使用例

const interests = [
  {
    name: "programming",
    emoji: "💻",
    score: 80,
  },
  {
    name: "motorcycle",
    emoji: "🏍"
    score: 45,
  },
];

上記のようなinterestsといった配列があった際にfilter関数は次のようにして使うことができます。

const a1 = interests.filter((x) => x.score >= 50);

結果

// a1の中身
[

  {
    name: "programming",
    emoji: "💻",
    score: 80,
  }
]

上記の処理ではinterestsから配列要素を1個ずつxとして取り出し、各要素のscoreが50以上のものをフィルタリングしてa1の変数に代入します。その結果scoreが80のprogrammingの要素全てが新しい配列として返されます。このようにfilterメソッドを使うことで、配列要素から条件に沿った要素のみをフィルタリングする処理を記述できます。

map()メソッド

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Maphttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map

与えられた関数を配列の全ての要素に対して呼び出し、その結果からなる新しい配列を生成します。つまり配列の「変換」に使う関数です。
配列要素に対して何らかの変換をする際に使います。

使用例

interests配列に対してfilterメソッドと同様にmapメソッドを次のようにして使います。

const a2 = interests.map((x) => x.name.toUpperCase());

結果

// a2の中身
[
  "PROGRAMMING",
  "MOTORCYCLE",
];

上記の処理ではinterestsの各要素のnameをtoUpperCaseメソッドを用いて大文字にします。その結果、各配列要素のnameであるprogrammingとmotorcycleが大文字にされ、配列として返されます。このようにmapメソッドを使うことで、配列の要素を取り出して変換処理ができます。
注意点としては今回の書き方だと配列要素のnameのみを参照しているので、配列要素の中のnameプロパティのみが変換され、新しい配列の要素となります。もし配列要素内のnameだけを大文字にして他のプロパティを残すにはスプレッド演算子などを用いて次のように記述する必要があります。

const a2 = interests.map((x) => ({...x, name: x.name.toUpperCase()}));

reduce()メソッド

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reducehttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

配列のそれぞれの要素に対してユーザーが提供した「縮小」コールバック関数を呼び出します。その際、直前の要素における計算結果の返値を渡します。配列の全ての要素に対して縮小関数を実行した結果が単一の結果が最終結果になります。つまり配列の「値の合成」に使う関数です。合計値や平均値など、配列の要素から合体した単一の値を求める際に使います。reduceのイメージとしては配列の次元を減らすといった意味があります。

使用例

先ほどと同様にinterests配列に対してreduce関数を次のようにして使います。

const a3 = interests.reduce((a, b) => a + b.score, 0);

結果

// a3の中身
125

上記の処理での関数内のうち1つ目の引数aは現時点での合計値を表し、2つ目の引数bは各配列要素を表します。また、reduceメソッドの第2引数の0はaの初期値を表します。こちらのように記述することで0として初期化されたaに対し、interests配列の各要素におけるscoreがたされていきます。そして最終的にscoreの合計値である125がa3として返されます。このようにreduceメソッドを工夫して使うことで平均や分散の値なども求めることができます。

filter・map・reduceの組み合わせ

最後にfilter・map・reduceの3つのメソッドを組み合わせて使う例を紹介します。

使用例

filterとmapは新しい配列を返すのでチェインメソッドを使用して、1回ずつ変数に代入してから次の処理を記述すること無く、連続した処理ができます。

const a4 = interests
  .filter((x) => x.score >=40)
  .map((x) => x.name.toUpperCase())
  .reduce((a, b) => a + b + ",", "")
  .slice(0, -1);

結果

// a4の中身
"PROGRAMMING,MOTORCYCLE"

上記の処理では、まずfilterメソッドでscoreが40以上かでフィルタリングします。次にmapメソッドでフィルタリングされた配列に対し、nameを大文字に変換した文字列の配列に変換します。そしてreduceメソッドでname文字列を","繋ぎで合成します。最後にsliceメソッドで合成された文字列最後の","を除去することで文字列"PROGRAMMING,MOTORCYCLE"を得ます。sliceメソッドは切り取りができる関数であり、(0, -1)と指定することで末尾の文字を除去できます。このようにリスト処理を組み合わせて使うことにより複雑な処理をワンライナーで書くことができます。

その他のリスト処理で使われるメソッド

JavaScriptの基本のリスト処理メソッドはfilter、map、reduceですが、その他のメソッドとしてはfind, slice、concat、flatMapなどが挙げられます。その他のメソッドに関して詳しくさらに知りたい方はこちらのQiitaの記事を参考にしてみてください。記事内の非破壊、破壊に関してはリスト処理後に新しい配列を生成するかしないかになっています。使用する際はできるだけ非破壊のメソッドを使っていくことをおすすめします。

https://qiita.com/Shokorep/items/929e2e66908eaa915286https://qiita.com/Shokorep/items/929e2e66908eaa915286

フレームワークでリスト処理が使われる場合

リスト処理は開発の現場でもよく使われます。以下は、JavaScriptの人気なフレームワークであるReactでmapが使用される例です。このように記述することで、todosの配列に入っている各値でリストの各要素を作ります。

const TodoList = ({ todos }) => {
   return todos.map(todo => {
      return (
         <li>
            {todo.id} title:{todo.title}
         </li>
      );
   });
};

リスト処理をサポートするライブラリ

こちらの2つはリスト処理をサポートするライブラリです。どちらも標準であるリスト処理以外の複雑な処理をまとめたライブラリになっています。これらのライブラリを使うことでより高度なリスト処理ができます。

lodash
https://lodash.com/https://lodash.com/

Underscore.js
https://underscorejs.org/https://underscorejs.org/

まとめ

今回はJavaScriptらしいリスト処理について解説しました。開発の現場でもよく使われる記法ですので、普段の開発でも積極的に使ってみてください。次回は非同期処理(基本編)の解説をします。