-
【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倍にする
データ
const nums = [1, 2, 3,];
.map()
const twicedNums = nums.map(n => n * 2);
console.log(twicedNums); // [ 2, 4, 6 ]
for...of
const twicedNums = [];
for (const n of nums) {
twicedNums.push(n * 2);
}
console.log(twicedNums); // [ 2, 4, 6 ]
条件に応じて要素を抽出する .filter()
Array.prototype.filter() – JavaScript | MDN
配列の要素ごとに、引数として渡した関数の処理を実施して、その関数の返り値がtruthyだった値を要素とした新しい配列を返します。
例
数列から奇数の数字だけを抽出する
データ
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9,];
.filter()
const odds = nums.filter(n => n % 2); // %は剰余。0はfalsy, 1はtruthy
console.log(odds); // [ 1, 3, 5, 7, 9]
for...of
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)を探す
データ
const myParty = [
{id: 1, name: "Dragonite"},
{id: 3, name: "Pidgeotto"},
{id: 0, name: "Pikachu"},
{id: 2, name: "Slowbro"},
];
.find()
const myFirstPokemon = myParty.find(({id}) => id === 0);
console.log(myFirstPokemon); // { id: 0, name: 'Pikachu' }
for...of
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を返します。
例
自分の好きなキャラクターの中にポケモンが含まれるかどうか調べる
データ
const myFavoriteCharacters = [
{
name: "Fukuda",
source: "Girls und Panzer"
},
{
name: "Swadloon",
source: "Pokemon"
},
{
name: "Kairu",
source: "Microsoft"
},
];
some()
const existsPokemon = myFavoriteCharacters.some(({source}) => source === "Pokemon");
console.log(existsPokemon); // true
for...of
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を返します。
例
自分の好きなキャラクターがポケモンだけかどうか調べる
データ
const myFavoriteCharacters = [
{
name: "Fukuda",
source: "Girls und Panzer"
},
{
name: "Swadloon",
source: "Pokemon"
},
{
name: "Kairu",
source: "Microsoft"
},
];
every()
const isOnlyPokemon = myFavoriteCharacters.every(({source}) => source === "Pokemon");
console.log(isOnlyPokemon); // false
for...of
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は「減らす」。配列の要素をひとつずつ削ってマージしていくイメージでしょうか(実際にもとの配列を破壊することはありませんが)。
例
自分の毎月の光熱費をすべて合計する
データ
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要素目の実行時には累積値が存在しないので、これを
const totalMyUtilityExpenses = myUtilityExpensesList.reduce(
// 第1引数が累積値、第2引数が配列の要素
(result, item) => result + item.fuel + item.light + item.water,
0
);
console.log(totalMyUtilityExpenses); // 33000
for...of
let totalMyUtilityExpenses = 0;
for (const item of myUtilityExpensesList) {
totalMyUtilityExpenses = totalMyUtilityExpenses + item.fuel + item.light + item.water
}
console.log(totalMyUtilityExpenses); // 33000
これについてはいろんな例を見たほうがよいので、MDNも読んでみてください。
関数のパイプなんかもできます。
// 任意の文字の次の文字を標準出力に出す
[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 帳票 要件定義 設計 電力小売業界