TypeScript実践力を身につける!コードスニペットで学ぶ型付けとインターフェース
- 1.JavaScriptのforEachメソッドとは?
- 2.forEachメソッドの使い方
- 3.forEachメソッドとfor文の違い
- 4.forEachメソッドの処理を抜ける方法は?
- 5.forEachメソッドで多次元配列を操作する方法は?
- 6.forEachメソッドで連想配列を操作できる?
- 7.forEachメソッド以外の配列操作方法
この記事では、JavaScriptにおけるforEachメソッドの基礎から応用まで紹介します。
forEachメソッドを習得すれば、プログラムを記述する際に必須な配列操作を自在に操作できるので、ぜひ最後までご覧ください。
1.JavaScriptのforEachメソッドとは?
forEachメソッドとは、他のループ処理と比べて配列操作に特化したループ処理になります。
配列の要素一つひとつに対して処理を行う場合などに有効です。
2.forEachメソッドの使い方
ここからはforEachメソッドの基本的な使い方を紹介します。
①基本構文
forEachメソッドは配列の要素を順番に取り出し、記述したコールバック関数に素って処理します。
forEachメソッドは配列が持つメソッドです。
そのため、配列名.forEach(処理)というように記述します。
let array1 = new Array("要素1", "要素2", "要素3");
array1.forEach(function(value){
console.log(value);
});
// 要素1
// 要素2
// 要素3
上記の例では各要素を順番に変数「value」へ格納し、出力しています。このように各要素に順番に処理を行う場合にforEachメソッドを利用しましょう。
②引数
forEachメソッドのコールバック関数にはvalue、index、arrayの3つが準備されています。
・value:配列の要素を順番に格納
・index:valueに格納されている要素のインデックスを格納
・array:forEachメソッドを呼び出している元の配列を格納
let array1 = new Array("要素1", "要素2", "要素3");
// 例1
array1.forEach(function(value, index){
console.log("value:" + value + ", index:" + index);
});
// value:要素1, index:0
// value:要素2, index:1
// value:要素3, index:2
//例2
array1.forEach(function(value, index, array){
console.log(array[index]);
});
// 要素1
// 要素2
// 要素3
例1ではvalueとindexを表示しました。ご覧の通り順番に要素、インデックスが格納されています。
例2では3つ目の引数arrayを表示しています。こちらの実行結果は先ほどの基本構文で表示したものと同じです。
例2ではvalueを使用していませんが、定義しています。これはコールバック関数内の1つ目の引数がvalueに、2つ目がindex、3つ目がarrayとなるためです。
③arrayの具体的な使い方
先ほどの例ではarrayを利用し、各要素を表示しました。
ここからはarrayの具体的な使い方を紹介します。
arrayに格納されているのはforEachメソッドを呼び出している元の配列と説明しました。
そのため、arrayの要素を変更すると元の要素も変更されます。
let before_array = new Array("要素1", "要素2", "要素3");
before_array.forEach(function(value, index, array){
array[index] = value + "を変更";
});
before_array.forEach(function(value){
console.log(value);
});
// 要素1を変更
// 要素2を変更
// 要素3を変更
上記のように、各要素に同じ変更を加えたい場合にarrayを利用しましょう。
④ラムダ式での記述方法
forEachメソッドはラムダ式で記述できます。
let array1 = new Array("要素1", "要素2", "要素3");
array1.forEach(value => console.log(value));
// 要素1
// 要素2
// 要素3
基本構文で紹介した例をラムダ式で記述すると、このように直観的に記述できます。
JavaScriptのラムダ式はアロー関数を用いて記述します。
上記の例ではコールバック関数が1列ですが、2列以上の場合は{}で囲む必要がある点に注意しましょう。
let array1 = new Array("要素1", "要素2", "要素3");
array1.forEach(value => {
console.log("要素を出力");
console.log(value);
});
// 要素を出力
// 要素1
// 要素を出力
// 要素2
// 要素を出力
// 要素3
3.forEachメソッドとfor文の違い
ここからは、forEachメソッドと同じループ処理である「for文」と比較します。
①配列の各要素を出力する場合を比較
配列の各要素を出力する例を紹介します。
let array1 = new Array("要素1", "要素2", "要素3", "要素4", "要素5");
//forEachメソッド
array1.forEach(function(value) {
console.log(value);
});
// 要素1
// 要素2
// 要素3
// 要素4
// 要素5
//for文(非推奨な書き方)
for(let i=0; i<array1.length; i++){
console.log(array1[i]);
}
// 要素1
// 要素2
// 要素3
// 要素4
// 要素5
//for文(推奨する書き方)
for(let value of array1) {
console.log(value);
}
// 要素1
// 要素2
// 要素3
// 要素4
// 要素5
forEachメソッドでの記述例を1つ、for文の記述例を2つ紹介しました。
すべての記述で配列内の全要素を出力しています。
簡単な処理の場合は、記述量に大きな差はありません。
forEachメソッドとfor文を比較し、最も大きな違いはforEachメソッドが配列専用のループ処理であることです。for文は配列以外でも使えるループ処理であることに対し、forEachメソッドは配列がないと利用できません。
また、for文については通常のfor文でなくfor-of文を推奨的な書き方として紹介しました。
通常のfor文は配列にアクセスする際の順番がインデックス順通りである保証がありません。要素数の多い配列アクセス時に順番に処理してくれない可能性があるため、for-of文を利用しましょう。
forEachメソッドが優れている点
配列の全要素を順番に処理したい場合はfor文と比べてforEachメソッドが優れています。
理由は下記の2点です。
・for文だと存在しないオブジェクトや不要なオブジェクトにアクセスできてしまう
・インデックスや配列自体など、要素以外の要素を取得しやすい
1点目の「for文だと存在しないオブジェクトや不要なオブジェクトにアクセスできてしまう」について、前述の通り、for文は配列が存在しない場合でもループ処理を記述できます。
そのため、書き方によっては配列の要素数以上のループ処理ができてしまい、存在しないオブジェクトにアクセスしエラーになります。
forEachメソッドの場合、存在する要素のみ取得するのでエラーになりにくいです。
2点目の「インデックスや配列自体など、要素以外の要素を取得しやすい」点については、まずはサンプルコードを見てみましょう。
let array1 = new Array("要素1", "要素2", "要素3", "要素4", "要素5");
//forEachメソッド
array1.forEach(function(value, index, array) {
console.log("要素:" + value + ",インデックス:" + index + ",元配列の値:" + array[index]);
});
// 要素:要素1,インデックス:0,元配列の値:要素1
// 要素:要素2,インデックス:1,元配列の値:要素2
// 要素:要素3,インデックス:2,元配列の値:要素3
// 要素:要素4,インデックス:3,元配列の値:要素4
// 要素:要素5,インデックス:4,元配列の値:要素5
//for文
let index = 0;
for(let value of array1) {
console.log("要素:" + value + ",インデックス:" + index + ",元配列の値:" + array1[index]);
index++;
}
// 要素:要素1,インデックス:0,元配列の値:要素1
// 要素:要素2,インデックス:1,元配列の値:要素2
// 要素:要素3,インデックス:2,元配列の値:要素3
// 要素:要素4,インデックス:3,元配列の値:要素4
// 要素:要素5,インデックス:4,元配列の値:要素5
上記の例では要素以外にインデックス、元の値を取得しています。forEachメソッドでは元々の引数で要素、インデックス、元の値を取得できます。
対してfor文には取得する方法が準備されていないので、別途変数を準備し再現する必要があるのです。
以上2点より、配列に対してループ処理ですべての要素に処理を行う場合は、forEachメソッドがおすすめです。
②ループ処理を中断する場合を比較
JavaScrptにはループ処理を中断するbreak文などがありますが、forEachメソッドで利用するとエラーになってしまいます。
let array1 = new Array("要素1", "要素2", "要素3", "要素4", "要素5");
//forEachメソッド
array1.forEach(function(value, index) {
if(index < 3){
console.log(value);
}
else {
break;
}
});
// Uncaught SyntaxError: Illegal break statement
ループ処理を中断させたい場合はfor文で書く必要があります。
4.forEachメソッドの処理を抜ける方法は?
前述の通りforEachメソッドを利用するとループ処理を中断できません。
ここでは、forEachメソッドでループ処理を中断したい場合の方法を2つ紹介します。
①for文に書き換える
forEachメソッドの処理はfor文に書き換えられます。
let array1 = new Array("要素1", "要素2", "要素3", "要素4", "要素5");
//forEachメソッド
array1.forEach(function(value, index) {
if(index < 3){
console.log(value);
}
else {
break;
}
});
// Uncaught SyntaxError: Illegal break statement
//for文
let index = 0;
for(let value of array1) {
if(index < 3){
console.log(value);
index++;
}
else {
break;
}
}
// 要素1
// 要素2
// 要素3
上記の例では、途中で中断させたかったがエラーになってしまうforEachメソッドの記述を、for文に書き換えています。
for文の中ではbreak文やcontinue文を利用できるので、ご覧のようにループ処理を中断できます。中断させたい場合はfor文を利用しましょう。
②someメソッドまたはeveryメソッドに書き換える
配列が持っているsomeメソッドやeveryメソッドを利用することでも、再現できます。
let array1 = new Array("要素1", "要素2", "要素3", "要素4", "要素5");
array1.some(function(value, index) {
console.log(value);
return index > 1;//trueになったら終了
});
// 要素1
// 要素2
// 要素3
array1.every(function(value, index) {
console.log(value);
return index < 2;//falseになったら終了
});
// 要素1
// 要素2
// 要素3
要素を出力後、条件を判定し中断しています。
someメソッドとeveryメソッドはtrue、falseのどちらで終了するかが異なります。
記述方法はforEachメソッドと同じ書き方です。
someメソッド、everyメソッドのほうがfor文と比べて直観的に変更しやすいでしょう。
5.forEachメソッドで多次元配列を操作する方法は?
多次元配列であっても、今まで同様の記述で要素一つひとつに処理を行うことが可能です。
let array1 = [
["要素1-1", "要素1-2", "要素1-3"],
["要素2-1", "要素2-2", "要素2-3"],
["要素3-1", "要素3-2", "要素3-3"],
]
array1.forEach(value => console.log(value));
// ["要素1-1", "要素1-2", "要素1-3"]
// ["要素2-1", "要素2-2", "要素2-3"]
// ["要素3-1", "要素3-2", "要素3-3"]
上記のように、要素の一つひとつを表示しているため、出力結果は配列になります。
6.forEachメソッドで連想配列を操作できる?
forEachメソッドは配列のメソッドであり、連想配列には存在しないメソッドです。よって、forEachメソッドで連想配列を操作しようとするとエラーになってしまいます。
そこで、Object.keys()を利用し連想配列のキーを配列に変換し利用しましょう。
let array1 = {name: "Taro", age: 20, birthplace: "Tokyo"}
Object.keys(array1).forEach(function(key){
console.log(key, array1[key]);
});
// name Taro
// age 20
// birthplace Tokyo
Object.keys(array1)でキーの配列[name, age, birthplace]を作成します。
キーを用いて連想配列から値を取得することで、forEachメソッドでも利用できるようになりました。
7.forEachメソッド以外の配列操作方法
ここまで配列操作にて万能なforEachメソッドを紹介してきましたが、ここからはforEachメソッドの代替案を紹介します。
①forEach以外の配列操作メソッドの紹介
map
配列の要素を用いて新たな要素を作成する場合、mapメソッドを利用します。
let nums = [1, 2, 3, 4, 5];
let add_2 = nums.map(num => num + 2);
console.log(add_2);
// [3, 4, 5, 6, 7]
ご覧の例では各要素に2を加えたadd_2を作成しています。
各要素に特定の操作を加えて配列を作成したい場合にmapメソッドを利用しましょう。
filter
配列から特定の要素を抽出したい場合、filterメソッドを利用します。
let fruits =["リンゴ","レモン", "ミカン", "ぶどう", "レモン"]
const lemons = fruits.filter(fruit => fruit == "レモン");
console.log(lemons);
// ['レモン', 'レモン']
フルーツリストからレモンのみを抽出しています。
比較のため、forEachメソッドでも記述してみましょう。
let fruits =["リンゴ","レモン", "ミカン", "ぶどう", "レモン"]
let lemons = [];
fruits.forEach(fruit =>{
if(fruit == "レモン"){
lemons.push(fruit);
}
});
console.log(lemons);
// ['レモン', 'レモン']
ご覧のように、filterメソッド利用時と比べてソースコードが長くなります。
また、if文で要素を判定しているため、条件式を確認しないと何を行っているかわかりません。
find
配列から特定の要素を探す場合、findメソッドを利用します。
先ほど紹介したfilterメソッドとの違いに注視して確認しましょう。
let fruits =["リンゴ", "レモン", "ミカン", "ぶどう", "レモン"]
const lemon = fruits.find(fruit => fruit == "レモン");
console.log(lemon);
//レモン
こちらのソースコードをfilterメソッドで記述してみましょう。
let fruits =["リンゴ", "レモン", "ミカン", "ぶどう", "レモン"]
const lemon = fruits.filter(fruit => fruit == "レモン")[0];
console.log(lemon);
//レモン
ほとんど同じソースコードの長さで実装できますね。
しかし、findメソッドを用いることで特定の要素が必要であることが明示されます。
filterメソッドの場合、欲しい結果が配列なのか1要素なのか判断する必要があります。
reduce
今まで紹介したメソッドと異なり、reduceメソッドは様々なことができます。
今まで紹介してきた3つのメソッドと比較して利用場面は少なくなりますが、覚えておきましょう。
まずは構文から紹介します。
const array2 = array2.reduce(コールバック, 初期値);
reduceメソッドでは今まで同様コールバック関数を指定し、第二引数で初期値を設定できます。初期値の設定は必須ではありませんが、出力結果が初期値と同じ型になるので、設定することをお勧めします。
実際の利用例を見てみましょう。
let nums = [1, 2, 3, 4, 5];
const my_sum = nums.reduce((prev, num) => prev + num, 0);
console.log(my_sum);
// 15
ご覧の例では、配列内要素の合計値を計算しています。
初めにprevに初期値の「0」、numに配列の1要素目「1」が。次に計算結果がprevに格納され、numに配列の2要素目「2」が格納されます。最終的なprevの値が出力され、15が格納されます。
forEachメソッドと異なり第二引数で型(今回は0のため数値)が判断できますので、最終的に求めている型が容易に判断できるのが特徴です。
②forEachメソッドを使わないメリット
forEachメソッドを利用しないメリットは2点あげられます。
・コードが簡潔になる
・何を行っているかがわかりやすい
1点目の「コードが簡潔になる」について、ソースコードが長くなってしまうと可読性が下がり、特にチームでコード作成している際などに思わぬ事故の原因となります。
また、コードの検証をする際などにもコードの可読性が高いほうが効率的に進められるでしょう。
2点目の「何を行っているかがわかりやすい」について、代替案として利用するメソッドは行える処理が限定されているため、一目で配列に対して行う操作内容がわかります。
1点目同様可読性が上がり、検証等の効率を上げられます。
以上2点より、利用シーンに応じて、代替案のメソッドを利用することを検討しましょう。