配列の反復処理メソッド - forEach, map等

《 初回公開:2023/10/19 , 最終更新:未 》

【 目次 】

Arrayクラスのメソッドには反復処理メソッド(Iterative methods)と呼ばれる配列の要素を先頭から順に走査して効果的に反復処理するためのさまざまなメソッドがサポートされている。
各メソッドはコールバック関数を受け取り、その関数が要素ごとに実行される。
これらのメソッドを使って、簡潔で効率的で読みやすいコードを記述する事ができる。

反復処理メソッドには以下のものがある。

  • forEach - 配列の要素を先頭から順番にひとつづつ取り出し処理する
  • map - 配列の各要素に対してコールバック関数を実行し、その結果から新しい配列を作成して返す
  • flatMap - map動作により生成された結果の配列をフラット化して返す
  • reduce - 配列の各要素を先頭(左)から順に累積演算をおこないその結果返す
  • reduceRight - 配列の各要素を最後(右)から順に累積演算をおこないその結果返す
  • every - 配列のすべての要素が与えられた条件を満たすかどうかを判定する
  • some - 要素のどれか一つでも条件を満たすかどうかを判定する
  • filter - 配列から要素をフイルタリング
  • find, findIndex, findLast, findLastIndex - 指定した条件を満たす最初(または最後)の要素を検索

反復処理メソッドは元の配列を変更しないので非破壊的メソッド。
find, findIndex ,findLast ,findLastIndex以外の反復処理メソッドは、空きスロットがあった場合に処理をスキップする。

forEach

forEachメソッドは配列の要素を先頭から順番にひとつづつ取り出し処理するメソッド。
各要素に対してコールバック関数を呼び出す。

配列の要素数分だけコールバック関数を実行する。

構文
array.forEach(callback(element, index, array)[, thisArg]);
引数
array
forEach メソッドを呼び出す配列自体。
callback
各要素に対して実行するコールバック関数。
コールバック関数の引数として
element
現在処理中の要素の値。
index
現在処理中の要素のインデックス。
array
forEach メソッドを呼び出す元の配列。
thisArg (省略可能)
コールバック関数内で this として参照する値を指定。
戻り値
undefinedを返す。
const array = ["a", "b", "c"];
array.forEach((element) => {
    console.log(element);
});
// 出力:
// a
// b
// c

配列に空きスロットがあった場合はスキップされて。

const array = ["a", "b", , "d"];
array.forEach((element) => {
    console.log(element);
});
// 出力:
// a
// b
// d

コールバック関数の引数をすべて使用すると

const array = ["a", "b", , "d"];
array.forEach((element, index, array) => {
    console.log(`element: ${element}, index: ${index}, array[${index}]: ${array[index]}`);
});
// 出力:
// element: a, index: 0, array[0]: a
// element: b, index: 1, array[1]: b
// element: d, index: 3, array[3]: d

thisArg引数について

forEachメソッドの第2引数thisArgを指定すると、コールバック関数内で this として参照する値となる。
どうもいい例が見当たらないが無理やり、サンプルプログラムをこしらえてみた。

const obj = { p: "xyz" };

const array = ["a", "b", "c"];
array.forEach(function (element) {
    console.log(element + this.p);
}, obj);
// axyz
// bxyz
// cxyz

thisArg引数にオブジェクトobjを指定すると、コールバック関数内で thisがobjを指すようになっている。
ただ、コールバック関数がアロー関数式の場合はthisArg引数は無視されてしまうので注意。

const obj = { p: "xyz" };

const array = ["a", "b", "c"];
array.forEach((element) => console.log(element + this.p), obj);
// aundefined
// bundefined
// cundefined

map

mapメソッドは配列の要素を先頭から順番に処理し、各要素に対してコールバック関数を呼び出すのはforEachメソッドと同じであるが、配列の各要素に対して与えられた関数を実行し、その結果から新しい配列を作成して返すところが異なる。

構文
array.map(callback(element, index, array)[, thisArg]);
引数
array
mapメソッドを呼び出す配列自体。
callback
各要素に対して実行するコールバック関数。
コールバック関数の引数として
element
現在処理中の要素の値。
index
現在処理中の要素のインデックス。
array
forEach メソッドを呼び出す元の配列。
戻り値
mapメソッドが生成する新しい配列の要素
thisArg (省略可能)
コールバック関数内で this として参照する値を指定。
戻り値
コールバック関数が返す値を要素とする新しい配列。

mapメソッドの構文はforEachメソッドとよく似ているがcallback関数が返す値が新しい配列の要素となる。
次のコードはmapメソッドを使って元の配列の各要素に1を加えた新しい配列を生成する例。

const array = [10,100,1000];

const array2 = array.map((element) => element + 1);
console.log(array2); // [11, 101, 1001]

mapメソッドは配列を返すのでメソッドチェインして他の配列メソッドと組み合わせることができる。

array.map((element) => element + 1).forEach((element) => console.log(element));
// 11
// 101
// 1001

コールバック関数が値を返さなかった場合、要素の値はundefinedに。

const array = [10, 100, 1000];
console.log(array.map(function () { }));    // [undefined, undefined, undefined]

flatMap

mapメソッドと flatメソッドを組み合わせたような動作。
コールバック関数を使ってmap動作により生成された結果の配列をflatメソッドのようにフラット化した配列を返す。

const array = [1, 2, 3];

const result = array.flatMap(element => [element, element * 2, element * 3]);
console.log(JSON.stringify(result)); // [1,2,3,2,4,6,3,6,9]

reduce

reduceメソッドは、配列の各要素に対して指定されたコールバック関数を実行し、配列の各要素の累積演算をおこないその結果返す。
配列はデータの集合と考えられるので平均値などの統計データの演算をおこなう等の利用が考えられる。

構文
array.reduce(callback[, initialValue]);
引数
array
reduceメソッドを呼び出す配列自体。
callback
各要素に対して実行するコールバック関数。
コールバック関数の引数として
element
現在処理中の要素の値。
index
現在処理中の要素のインデックス。
array
forEach メソッドを呼び出す元の配列。
戻り値
配列の各要素の累積演算の結果の値。
initialValue (省略可能)
accumulatorの初期値として使用。initialValue を指定しない場合、最初の要素が accumulator の初期値となり、2番目の要素から callback 関数が実行される。
戻り値
コールバック関数から最後にreturnされた値。

簡単な例 - 配列の全要素を加算

const array = [10, 100, 1000];
const sum = array.reduce((accumulator, element) => accumulator + element, 0);
console.log(sum); // 1110

余談だが、reduceは減らすとか縮小するという意味。
reduceメソッドは、関数型プログラミングやリスト処理の文脈でよく使われる操作で、
このメソッドは、配列の要素を累積的に結合して単一の値を生成するために使用される。
そのため、このメソッドは要素を「縮小」または「集約」していくというイメージで命名されたようだ。
配列の要素を次の要素と繰り返し結合していく過程で、配列の要素が「縮小」され、最終的に単一の値に「集約」されるという意味。

上記の「配列の全要素を加算」の例の場合、以下のように初期値initialValueを省略する事ができて。

const sum = array.reduce((accumulator, element) => accumulator + element);

reduceメソッドの引数 initialValueは省略可能であるが、省略した場合の動作は異る。
initialValueを省略した場合、配列の最初の要素が初回の累積値(accumulator)として使用される。
その後、配列の2番目の要素から処理が始まる。

複数の演算結果を返す例、
以下のコードは配列の要素の最小値,最大値,合計,平均値を求める例である。

const array = [10, 100, 1000];
const result = array.reduce(function (accumulator, element, index, array) {
    if (accumulator.min > element) accumulator.min = element;
    if (accumulator.max < element) accumulator.max = element;
    accumulator.sum += element;
    if (index == array.length - 1)
        accumulator.avg = accumulator.sum / array.length;
    return accumulator;
}, { min: Infinity, max: -Infinity, sum: 0 });
console.log(JSON.stringify(result));    // {"min":10,"max":1000,"sum":1110,"avg":370}

reduceRight

reduceメソッドが配列の要素を先頭(左)から順番に処理するのに対してreduceRightメソッドは最後(右)から先頭の要素に向かって処理をおこなう。
以下のコードはreduceメソッドとreduceRightメソッドの動作の違いを示している。

const array = ["a", "b", "c"];

const str = array.reduce((accumulator, element) => accumulator + element);
console.log(str);   // abc

const strRight = array.reduceRight((accumulator, element) => accumulator + element);
console.log(strRight);  // cba

every

everyメソッドは、配列のすべての要素が与えられた条件を満たすかどうかを判定するために使用される。
everyメソッドはブール値を返し、条件がすべての要素に対して真である場合は true を、少なくとも1つの要素で条件が偽である場合は false を返す。

everyメソッドはコールバック関数を受け取る。
コールバック関数は、配列の各要素とそのインデックス,配列自体を引数として受け取り、条件を評価する。
コールバック関数の戻り値は論理値として評価され、真であれば次の要素に進み、偽であれば処理を終了し、false を返す。

構文
array.every(callback(element, index, array)[, thisArg]))
引数
引数の意味はforEachやmapと同様。
戻り値
配列のすべての要素が与えられた条件を満たすかどうかを示す論理値。

以下のコードは配列の要素がすべて奇数かどうかを判定する。

isOdd = value => value % 2;

var result = [2, 3, 5].every(isOdd);
console.log(result);    // false

var result = [1, 3, 5].every(isOdd);
console.log(result);    // true

some

everyメソッドがすべての要素が与えられた条件を満たすかどうかを判定するのに対して、someメソッドは要素のどれか一つでも条件を満たすかどうかを判定する。

構文
array.some(callback(element, index, array[, thisArg]))
引数
引数の意味はforEachやmapと同様。
戻り値
配列の要素のどれか一つが与えられた条件を満たすかどうかを示す論理値。

以下のコードは配列の要素のどれか一つが奇数かどうかを判定する。

isOdd = value => value % 2;

var result = [2, 3, 8].some(isOdd);
console.log(result);    // true

var result = [2, 4, 8].some(isOdd);
console.log(result);    // false

filter

元の配列から要素をフイルタリング。
与えられた条件に合致する要素のみを持つ新しい配列を作成する。
選択された新しい配列の要素は元の要素のシャローコピー(浅いコピー)となる。

コールバック関数は配列の要素を先頭から順番に処理し、フィルタ条件に合うかどうかの真偽値を返えすものとする。

構文
array.some(callback(element, index, array[, thisArg]))
引数
引数の意味はforEachやmapと同様。
戻り値
元の配列から要素をフイルタリングした結果の配列。

以下は元の配列の要素から奇数要素だけを取り出す。

var result = [2, 3, 5, 8].filter(value => value % 2);
console.log(JSON.stringify(result));    // [3, 5]

新しい配列の要素はシャローコピーであるため、配列の要素がオブジェクトの場合、新しい配列の要素のプロパティを変更すると元の配列の要素も変更されるので注意。

let array=[new Object()];
let result = array.filter(() => true);

// 新しい配列の要素のプロパティを変更
result[0].prop="xxx";

// 元の配列の要素も変更される
console.log(JSON.stringify(array[0].prop)); // "xxx"

find,findIndex,findLast,findLastIndex

条件を満たす最初(または最後)の要素を検索する。

これらのメソッドは共通のパターンを持っている。
各メソッドはコールバック関数を受け取り、配列内の要素を順番に評価。
コールバック関数は各要素とそのインデックスを引数として受け取り、真偽値を返えす。
最初(または最後)の要素から順に評価され、最初に真を返す要素 (または最後に真を返す要素) を返す。

callback は空きスロットに対しても呼び出されるため、空きスロットをスキップするメソッドと比較して効率的ではない。

find(callback, thisArg)

配列内の最初の要素を見つけるために使用される。
与えられたコールバック関数が真を返す最初の要素を返す、見つからない場合は undefined。

引数
引数の意味はforEachやmapと同様。
callback関数の戻り値
各要素やインデックスが条件にマッチしているかどうかを示す真偽値。
メソッドの戻り値
配列内の最初にマッチした要素。

findIndex(callback, thisArg)

findメソッドと似ているが、要素のインデックスを返す点が異なる。
与えられたコールバック関数が最初に真となる要素のインデックスを返し、見つからない場合は -1 を返す。

findLast(callback, thisArg)

配列内の最後の要素を見つけるために使用される。
与えられたコールバック関数が真を返す最後の要素を返す、見つからない場合は undefined。

findLastIndex(callback, thisArg)

findLastメソッドと似ているが、要素のインデックスを返す点が異なる。
与えられたコールバック関数が最後に真を返す要素のインデックスを返し、見つからない場合は -1 を返す。

サンプルコード

奇数の要素を検索

//奇数かどうかを判定する関数
isOdd = value => value % 2;

// find
var result = [2, 3, 5, 8].find(isOdd);
console.log(result);    // 3
var result = [2, 4, 6, 8].find(isOdd);
console.log(result);    // undefined

// findIndex
var result = [2, 3, 5, 8].findIndex(isOdd);
console.log(result);    // 1
var result = [2, 4, 6, 8].findIndex(isOdd);
console.log(result);    // -1

// findLast
var result = [2, 3, 5, 8].findLast(isOdd);
console.log(result);    // 5
var result = [2, 4, 6, 8].findLast(isOdd);
console.log(result);    // undefined

// findIndex
var result = [2, 3, 5, 8].findLastIndex(isOdd);
console.log(result);    // 2
var result = [2, 4, 6, 8].findLastIndex(isOdd);
console.log(result);    // -1
ページのトップへ戻る