【保存版】JavaScriptの配列のループの方法を全て覚えとこう
はじめに
前回、JavaScriptでの基本となる配列と連想配列(オブジェクト)の取り扱いについて学びました。
配列やオブジェクトのループ処理はGASを使う上でも必ず必須になります。
最後には、担当者ごとの営業売上を集計する練習問題も用意しました。最後まで楽しくやっていきましょう!
忘れたら何回でもこのnoteに戻ってきてください。
配列のループの方法一覧
今回は5種類紹介します。
1. forEachを使う方法
2. for inを使う方法
3. forを使う方法
4. for ofを使う方法
5. for ofとArray.entriesを使う方法
今回はこのような、生徒の科目ごとの点数が格納されてる二次元配列を利用していきます。
let points = [
["西田", "数学", 20],
["西田", "国語", 98],
["寺西", "数学", 45],
["寺西", "国語", 60],
]
1. forEachを使う方法
まずは、forEachを使ってループ処理してみましょう。
/**
* forEachを使う方法
*/
points.forEach((val, i) => {
console.log(i, val)
})
forEachの引数は、順番に、「value」「index」「array」「thisArg」を受け取りますが、valueだけが必須です。
今回は、indexとvalueを出力してみました。
0 [ '西田', '数学', 20 ]
1 [ '西田', '国語', 98 ]
2 [ '寺西', '数学', 45 ]
3 [ '寺西', '国語', 60 ]
このような結果です。indexはいつもどおり0から始まってるのが分かりますね。
※ 因みに、forEachは途中でbreakして処理を中断することができないので、そのような用途では使いません。
2. for inを使う方法
さぁ、次にfor inを使う方法を見てみましょう。
/**
* for inを使う方法
*/
for (let i in points) {
console.log(i, points[i])
}
一般的なfor文に似ててシンプルですね。直感的に使えるかと思います。
0 [ '西田', '数学', 20 ]
1 [ '西田', '国語', 98 ]
2 [ '寺西', '数学', 45 ]
3 [ '寺西', '国語', 60 ]
ちなみに、for inを使うと途中で処理を止めることができます。例えば、80点以上の優等生を見つけたらその場で処理を止めてみましょう。
(すぐに褒めてあげましょう!w)
/**
* for inを使う方法
*/
for (let i in points) {
// 点数は各配列の3番目に入っている
if (points[i][2] >= 80) {
console.log(`${points[i][0]}!凄いじゃないか!${points[i][1]}で、${points[i][2]}点も取ってるぞ!`)
break;
}
console.log(i, points[i])
}
こんな感じで、80点超えの生徒がいたら褒めて、以降の生徒を無視する処理を入れてみましょう(ひどい先生ですね...w)
0 [ '西田', '数学', 20 ]
西田!凄いじゃないか!国語で、98点も取ってるぞ!
【余談】ES2015以降の文字列の中での変数展開について
これまでのJavaScriptでは、今回のような、「西田」が入っている「points[i][0]」という変数と「凄いじゃないか!」という繋げて出力をしたい場合は以下のように長ったらしく書く必要がありました。
console.log(points[i][0] + '!凄いじゃないか!' + points[i][1] + 'で、' + points[i][2] + '点も取ってるぞ!')
かなり面倒臭いですよね。それがES2015から文字列の中で変数を展開できるテンプレートが用意されました。
「``」の中で、変数を展開したい場合は、「${変数名}」を使えば展開されます。
let name = "西田"
let point = "98"
console.log(`${name}くん!君の点数は${point}だったよ!`)
/**
* 出力結果
* => 田くん!君の点数は98だったよ!
*/
こんな感じです。便利なので覚えちゃいましょう!
3. forを使う方法
これも一般的なfor文なのでシンプルです。
/**
* forを使う方法
*/
for (let i = 0; i < points.length; i++) {
console.log(i, points[i])
}
配列の長さは、Array.lengthで取得できるので、今回は「points.length」を使っています(この場合4が返ってきてます)
↓出力結果
0 [ '西田', '数学', 20 ]
1 [ '西田', '国語', 98 ]
2 [ '寺西', '数学', 45 ]
3 [ '寺西', '国語', 60 ]
ちなみにfor文もfor inと同様にbreakによって処理を止めることができます。
4. for ofを使う方法
for ofは以下のように書きますが、書き方自体はfor inと非常に似てますが、ループの中で参照できる値がindexではなくて、valueになるのが特徴です。
/**
* for of を使う方法
*/
for (let val of points) {
console.log(val)
}
for inの場合は、ループの中で参照できるのはindexの値だったので、valueを取得しようと思うと、「points[i]」のようにする必要があり冗長でしたが、for orを使えば、「val」だけで参照できます。
でも、for ofじゃindexの値が参照できないじゃん?と思った方への答えが次のfor ofとArray.entriesを使う方法です。
5. for ofとArray.entriesを使う方法
/**
* for ofとArray.entriesを使う
* 添字と値を一緒に使いたい場合
*/
for (let [i, val] of points.entries()) {
console.log(i, val)
}
このように書けば、indexの値も使うことができます。
0 [ '西田', '数学', 20 ]
1 [ '西田', '国語', 98 ]
2 [ '寺西', '数学', 45 ]
3 [ '寺西', '国語', 60 ]
取れましたね。
entries() メソッドとは?
points.entriesって何?と思ったと思います。
これは公式の文章を借りるとこういう説明です。
entries() メソッドは、配列内の各要素に対する key/value ペアを含む新しい Array Iterator オブジェクトを取得します。
分かりづらいですが、「ループ処理ができるindexとvalueのセットをお返ししますよ」というものです。
これも、分かりづらいですし、処理の時間もかかるので、indexとvalueの両方を使いたい場合は、forEachを使うのが無難ですね。
番外編 : 各ループ処理の速度
速度がどれくらい違うのかも見ておきましょう。
ちなみに、console.time('hoge')とconsole.timeEnd('hoge')とすると、この2つで囲まれた中でかかった処理の時間を計測してくれます。
使うコードはこのようにしました。
let points = []
for (let i = 0; i < 100; i++) {
points.push([i, i+1, i+2])
}
// 一時的に代入用に変数を用意
let tmp = []
console.time('forEach')
points.forEach((val, i, a) => {
tmp.push([i, val])
})
console.timeEnd('forEach')
tmp = []
console.time('for in')
for (let i in points) {
tmp.push([i, points[i]])
}
console.timeEnd('for in')
tmp = []
console.time('for')
for (let i = 0; i < points.length; i++) {
tmp.push([i, points[i]])
}
console.timeEnd('for')
tmp = []
console.time('for of')
for (let val of points) {
tmp.push([val])
}
console.timeEnd('for of')
tmp = []
console.time('for of + entries')
for (let [i, val] of points.entries()) {
tmp.push([i, val])
}
console.timeEnd('for of + entries')
100件分の配列の処理をした時の結果
forEach: 0.201ms
for in: 0.227ms
for: 0.136ms
for of: 0.185ms
for of + entries: 0.23ms
ランキングはこんな感じです。forが圧倒的ですね。
1位: for (0.136ms)
2位: for of (0.185ms)
3位: forEach (0.201ms)
4位: for in (0.227ms)
5位: for of + entries (0.23ms)
10000件だとどうでしょうか。
forEach: 19.135ms
for in: 4.667ms
for: 3.281ms
for of: 3.319ms
for of + entries: 6.913ms
かなり結果がばらつきました。
1位: for (3.281ms)
2位: for of(3.319ms)
3位: for in(4.667ms)
4位: for of + entries(6.913ms)
5位: forEach(19.135ms)
forEachが特にパフォーマンスが悪いですね。
最近のwebサイトは表示速度もGoogleの検索順位を考慮する項目の1つになっているので、この辺も頭に入れつつ実装していきましょう!
理解度チェックテスト① - 担当者ごとの売上を出力してみよう
以下のコードを書いて、Twitterで#スキプラチャレンジを付けてツイートしてみてください。(※ 回答のコードはスクショとかでもOKです!必ずコメント返します)
以下のような配列があった時、担当者ごとの合計の売上を計算して出力してください。
let revenue = [
['西田', 10000],
['西田', 20000],
['寺西', 1500],
['寺西', 30000],
]
※ 「担当者ごとの」を実現するにはオブジェクトを使う方が楽です
↓ 答えのイメージ
{ '西田': 30000, '寺西': 31500 }
ちょっとむずかしいかも?って人はTwitterで連絡ください。
ヒント出します。
最後にお願い
ぜひぜひ、今後のモチベーションのためにもお願いします!!
もしこのnoteが参考になったら....
■ 最後にたった2つのお願い
① このnoteを「スキ」&シェアしてください 🙇♂️
② このnoteの感想、何でも良いので @riman_skillplus 宛にください 🙇♂️
https://twitter.com/riman_skillplus
実践編で利用するための基礎編はすべて無料公開してます。 基礎編のモチベーション向上のためにサポートして頂けるとめちゃくちゃ喜びます!! だいたい作業工数は1記事あたり4-5時間程度かけて【分かりやすい】【知識が身につく!】を意識して作っておりますので、今後も頑張っていきます!