-
【JavaScript】配列をfor文で回すのはもうやめよう。配列の高階関数なメソッドを使いこなす
- 2020年1月22日
- JavaScript
for文(for…ofを前提として書きますが)では、「配列(かなにかiterableなもの)を1件ずつ処理している」以上の情報は、その内容を読み込まないと得られません。
Array.prototype
に存在する高階関数なメソッドを使用すると、ざっくりと「新しい配列を作っているんだな」とか「集計してるんだな」とかのある程度の処理内容を読み手に伝えることができます。
その上、目的のデータ形式を得るための書き方が決まっていて、必然的にconstを使いやすくなるというメリットもあります。
高階関数?
高階関数とは、関数を引数に取る関数のことです。
JavaScriptは、関数が第一級オブジェクトな言語なので、関数を変数に格納したり、引数として渡したり、関数の結果として返したりすることができます。
配列の高階関数なメソッドたち
配列のメソッドには、この高階関数の形をとって、
- 配列の要素ごとに
- 引数として渡した関数の処理を適用して
- そのメソッドに応じた結果を返す
という挙動をするものがあります。それらしい名前がついているので、なんとなく何が行われているかわかります。
以下のメソッドを紹介します。
.map()
.filter()
.find()
.some()
.every()
.reduce()
また、以下の例ではオブジェクトの分割代入(MDN)を使用します。
免責
細かい使い方はMDNをご覧ください。
新しい配列を作る .map()
Array.prototype.map() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を適用して、その関数の返り値を要素とした新しい配列を返します。
例
数列の数字をそれぞれ2倍にする
データ
1 | const nums = [1, 2, 3,]; |
.map()
1 2 | const twicedNums = nums.map(n => n * 2); console.log(twicedNums); // [ 2, 4, 6 ] |
for...of
1 2 3 4 5 | const twicedNums = []; for (const n of nums) { twicedNums.push(n * 2); } console.log(twicedNums); // [ 2, 4, 6 ] |
条件に応じて要素を抽出する .filter()
Array.prototype.filter() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値がtruthyだった値を要素とした新しい配列を返します。
例
数列から奇数の数字だけを抽出する
データ
1 | const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9,]; |
.filter()
1 2 | const odds = nums.filter(n => n % 2); // %は剰余。0はfalsy, 1はtruthy console.log(odds); // [ 1, 3, 5, 7, 9] |
for...of
1 2 3 4 5 | const odds = []; for (const n of nums) { if (n % 2) odds.push(n) } console.log(odds); // [ 1, 3, 5, 7, 9] |
条件に合致した要素を返す .find()
Array.prototype.find() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値が最初にtruthyになった要素を返します。
例
自分の手持ちから、初めて捕まえたポケモン(idが0)を探す
データ
1 2 3 4 5 6 | const myParty = [ {id: 1, name: "Dragonite" }, {id: 3, name: "Pidgeotto" }, {id: 0, name: "Pikachu" }, {id: 2, name: "Slowbro" }, ]; |
.find()
1 2 | const myFirstPokemon = myParty.find(({id}) => id === 0); console.log(myFirstPokemon); // { id: 0, name: 'Pikachu' } |
for...of
1 2 3 4 5 6 7 8 | let myFirstPokemon; for (const p of myParty) { if (p.id === 0) { myFirstPokemon = p; break ; } } console.log(myFirstPokemon); // { id: 0, name: 'Pikachu' } |
論理和を返す .some()
Array.prototype.some() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値がひとつでもtruthyになればtrueを返します。
例
自分の好きなキャラクターの中にポケモンが含まれるかどうか調べる
データ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const myFavoriteCharacters = [ { name: "Fukuda" , source: "Girls und Panzer" }, { name: "Swadloon" , source: "Pokemon" }, { name: "Kairu" , source: "Microsoft" }, ]; |
some()
1 2 | const existsPokemon = myFavoriteCharacters.some(({source}) => source === "Pokemon" ); console.log(existsPokemon); // true |
for...of
1 2 3 4 5 6 7 8 | let existsPokemon = false ; for (const {source} of myFavoriteCharacters) { if (source === "Pokemon" ) { existsPokemon = true ; break ; } } console.log(existsPokemon); // true |
論理積を返す .every()
Array.prototype.every() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値がすべてtruthyになればtrueを返します。
例
自分の好きなキャラクターがポケモンだけかどうか調べる
データ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const myFavoriteCharacters = [ { name: "Fukuda" , source: "Girls und Panzer" }, { name: "Swadloon" , source: "Pokemon" }, { name: "Kairu" , source: "Microsoft" }, ]; |
every()
1 2 | const isOnlyPokemon = myFavoriteCharacters.every(({source}) => source === "Pokemon" ); console.log(isOnlyPokemon); // false |
for...of
1 2 3 4 5 6 7 8 | let isOnlyPokemon = true ; for (const {source} of myFavoriteCharacters) { if (source !== "Pokemon" ) { isOnlyPokemon = false ; break ; } } console.log(isOnlyPokemon); // false |
処理結果を積み重ねていく .reduce()
Array.prototype.reduce() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値をひとつの変数に集積した結果を返します。
これだけ少し特殊です。関数の書き方によって何でもできます。今まで出てきたメソッドなんかも実装できます。
reduceは「減らす」。配列の要素をひとつずつ削ってマージしていくイメージでしょうか(実際にもとの配列を破壊することはありませんが)。
例
自分の毎月の光熱費をすべて合計する
データ
1 2 3 4 5 | const myUtilityExpensesList = [ { month: "Jan" , fuel: 5000, light: 3000, water: 2500 }, { month: "Feb" , fuel: 4600, light: 3200, water: 3000 }, { month: "Mar" , fuel: 5200, light: 4000, water: 2800 }, ]; |
.reduce()
.reduce()
の第1引数にコールバック関数、第2引数に結果の初期値(後述)を渡します。- 第1引数に渡したコールバック関数の第1引数に最終結果となる累積値、第2引数に配列の各要素を渡します。
- 関数内でreturnされた値が、つぎの要素での累積値として扱われます。
- そのため、1要素目の実行時には累積値が存在しないので、これを
.reduce()
の第2引数で代替します。
- そのため、1要素目の実行時には累積値が存在しないので、これを
1 2 3 4 5 6 | const totalMyUtilityExpenses = myUtilityExpensesList.reduce( // 第1引数が累積値、第2引数が配列の要素 (result, item) => result + item.fuel + item.light + item.water, 0 ); console.log(totalMyUtilityExpenses); // 33000 |
for...of
1 2 3 4 5 | let totalMyUtilityExpenses = 0; for (const item of myUtilityExpensesList) { totalMyUtilityExpenses = totalMyUtilityExpenses + item.fuel + item.light + item.water } console.log(totalMyUtilityExpenses); // 33000 |
これについてはいろんな例を見たほうがよいので、MDNも読んでみてください。
関数のパイプなんかもできます。
1 2 | // 任意の文字の次の文字を標準出力に出す [str => str.codePointAt(0) + 1, String.fromCharCode, console.log].reduce((data, func) => func(data), "A" ); // 'B' |
おわり
楽しい配列ライフを!
この記事を書いた人 : 池上龍一
AWS bluebird css CSV docker docker compose electron ES6 es2015 Git Heroku ITコンサルティング JavaScript justinmind less MongoDB Node.js php PostgreSQL Private Space Promise React react-router reactjs Salesforce scss Selenium Builder selenium IDE Selenium WebDriver stylus TypeScript VirtualBox VisualStudioCode vue vuejs webpack システム開発プロジェクト セキュリティ ワイヤーフレーム 上流工程 卒FIT 帳票 要件定義 設計 電力小売業界