吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】配列について Vol.2(配列の要素ごとに関数を実行するメソッド)

2018年2月16日 / category : javascript

Arrayオブジェクトで定義されているメソッドの中で、配列の要素をループし、各要素ごとに関数を実行するメソッドがいくつかあります。今回の記事ではそれらのメソッドを解説して行きます。まずは、基本形となるメソッドforEachを使用したコードを見てみます。

メソッドforEach

array.forEach(func, object):指定した関数を各要素に対し実行する
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
構文

[1,2,3,4].forEach(function(value, index, arr){
	console.log(value, index, arr);
});

sample.js
const data = ['あ','い','う','え','お'];
function debug(value, index, arr){
	console.log( value, index, arr, this );
};
data.forEach(debug);

コンソール画面

あ 0 (5) ["あ", "い", "う", "え", "お"] Window {...}
い 1 (5) ["あ", "い", "う", "え", "お"] Window {...}
う 2 (5) ["あ", "い", "う", "え", "お"] Window {...}
え 3 (5) ["あ", "い", "う", "え", "お"] Window {...}
お 4 (5) ["あ", "い", "う", "え", "お"] Window {...}

上のコードで、メソッドforEachで各要素に対し関数を実行した際の引数の値と関数内のthisの参照を確かめてみました。対象となる配列dataの要素は5個ですので、メソッドforEachで実行されるループ処理は5回です。実行される関数debugの結果を見てみると、引数valueには配列の要素が参照されています。引数indexにはインデックス、引数arrには対象となっている配列が参照されています。関数debug内のthisに関しては、メソッドforEachの第2引数には何も設定していないので、thisはWindowオブジェクトを参照します。続いて、メソッドforEachの第2引数にオブジェクトを設定してみます。

sample.js
const data = ['あ','い','う','え','お'];

class Test{};
let t = new Test();

function debug(value, index, arr){
	console.log( this ); // Test {}
};
data.forEach(debug, t);

Testクラスを定義し、変数tにTestオブジェクトを生成しました。その変数tをメソッドforEachの第2引数に設定しました。実行される関数debug内のthisの参照がTestオブジェクトになったことが確認できました。

sample.js
const data = [1,2,3,4,5,6,7,8,9];

class Test{
	constructor(){
		this.total = 0;
	}
};
let t = new Test();

function add(value){
	this.total += value;
};
data.forEach(add, t);

console.log( t.total ); // 45

上のコードでは、配列dataの全ての要素をTestオブジェクトのプロパティtotalに追加しています。1+2+3+4+5+6+7+8+9という計算になるため、結果45が出力されています。
これが、配列の要素をループし、各要素ごとに関数を実行するメソッドの基本形になります。続いて、メソッドsomeの解説をします。

メソッドsome

array.some(func, object):指定した関数を各要素に対し実行し、条件を満たすかどうかをテストします。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 実行される関数のreturnの値(条件文)のいずれかがtrueを返した場合trueを返します。全てfalseだった場合falseを返します。
構文

let flag = [1,2,3,4].some(function(value, index, arr){
	return value > 2;
});

sample.js
const data = [100,200,300,400,500,600,700,800,900];

function debug(value, index){
	console.log( index, value );
	return value > 500;
};

let flag = data.some(debug);

console.log( flag ); // true

コンソール画面

0 100
1 200
2 300
3 400
4 500
5 600
true

メソッドsomeの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。メソッドsomeには戻り値があります。この戻り値は、実行される関数のreturnの値の条件の結果に影響されます。もし条件結果がtrueの場合、ループ処理が止まり、trueを返します。上のコードを見ると、returnの値は、要素が500以上かどうかの条件文になっています。インデックス5のとき、要素は600です。このタイミングで条件がtrueになりますので、ループ処理が止まり、trueを返します。
続いて、メソッドeveryについて解説します。

メソッドevery

array.every(func, object):指定した関数を各要素に対し実行し、全ての要素が条件を満たすかどうかをテストします。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 実行される関数のreturnの値(条件文)のいずれかがfalseを返した場合falseを返します。全てtrueだった場合trueを返します。
構文

let flag = [1,2,3,4].every(function(value, index, arr){
	return value > 2;
});

こちらもメソッドsomeと同様に、メソッドの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。このメソッドeveryはメソッドsomeと逆の処理をします。次のコードを見てみます。

sample.js
const data = [100,200,300,400,500,600,700,800,900];

function debug(value, index){
	console.log( index, value );
	return value > 500;
};

let flag = data.every(debug);

console.log( flag ); // false

コンソール画面

0 100
false

メソッドsomeのコードと同じです。違いは実行しているメソッドをsomeからeveryに変えただけです。このメソッドeveryは、returnの値となる条件文がtrueである限り、ループ処理が行われますが、条件文がfalseになったタイミングでループ処理が止まり、falseを返します。上のコードの場合、インデックス0(最初のループ処理)に要素が100のため、条件に満たしておらず、falseになります。そのため、最初のループ処理でループが止まり、falseを返しています。

メソッドsome、メソッドeveryは、対象の配列の要素を条件に満たしているかどうかをテストするメソッドとなります。

続いて、メソッドmapについて解説します。

メソッドmap

array.map(func, object):各要素に関数を実行し、要素を変更させ、新しい配列を生成する。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 対象となる配列の要素を変更させ、新たに生成した配列(array)
構文

const newArr = [1,2,3,4].map(function(value, index, arr){
	return value + 2;
});

sample.js
const textArr = ['あ','い','う','え','お'];
const tagArr = textArr.map( function(value){
	return '<span>'+value+'</span>';
} );
console.log( tagArr ); // (5) ["<span>あ</span>", "<span>い</span>", "<span>う</span>", "<span>え</span>", "<span>お</span>"]

このメソッドmapも、メソッドの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。上のコードでは、1文字づつのデータをまとめた配列に対し、各要素にspanタグの文字列を追加し、returnしています。この処理で「あ」の要素が「<span>あ</span>」になります。これを全ての要素で実行し、その結果を新しい配列を生成し返します。続いて、メソッドfilterについて解説します。

メソッドfilter

array.filter(func, object):各要素に関数を実行し、条件を満たした要素で新しい配列を生成する。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 対象となる配列の要素を変更させ、新たに生成した配列(array)
構文

const newArr = [1,2,3,4].filter(function(value, index, arr){
	return value > 2;
});

sample.js
const data = [100,200,300,400,500,600,700,800,900];
function test(value){
	return value > 500;
};
const newArr = data.filter(test);
console.log( newArr ); // (4) [600, 700, 800, 900]

このメソッドfilterも、メソッドの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。上のコードでは、配列dataの要素に対し、500以上かどうかの条件をテストする関数が実行されます。そして、条件を満たした要素のみをまとめた新しい配列newArrを生成しています。
続いて、メソッドfindの解説をします。

メソッドfind

array.find(func, object):各要素に関数を実行し、条件を満たした要素が見つかったタイミングでその値を返す。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 条件を満たした要素
構文

let num = [1,2,3,4].find(function(value){
	return value > 2;
});

sample.js
const data = ['あいうえお','かきくけこ','さしすせそ','たちつてと','なにぬねの','はひふへほ'];
function search(value){
	return value.indexOf('なに') !== -1;
};
let value = data.find(search);
console.log( value ); // なにぬねの

このメソッドfindも、メソッドの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。上のコードでは、配列dataの要素に対し、「なに」の文字列があるかどうかを判別しています。条件を満たした要素(「なにぬねの」)が見つかったタイミングで要素「なにぬねの」を返しています。
続いて、メソッドfindIndexの解説をします。

メソッドfindIndex

array.findIndex(func, object):各要素に関数を実行し、条件を満たした要素が見つかったタイミングでそのインデックスを返す。
引数 func(function) : 各要素に実行する関数
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 object(object) : 関数内でthisを指すオブジェクト(任意)
戻り値(return) : 条件を満たしたインデックス
構文

let index = [1,2,3,4].findIndex(function(value){
	return value > 2;
});

sample.js
const data = ['あいうえお','かきくけこ','さしすせそ','たちつてと','なにぬねの','はひふへほ'];
function search(value){
	console.log(value);
	return value.indexOf('なに') !== -1;
};
let index = data.findIndex(search);
console.log( index ); // 4

このメソッドfindIndexも、メソッドの引数、各要素で実行される関数の引数の仕組みはメソッドforEachと同様です。使い方はメソッドfindと同じです。ただ、返ってくる値が要素ではなく、インデックスになります。
続いて、メソッドreduceの解説をします。このメソッドの引数は、メソッドforEachと異なります。

メソッドreduce

array.reduce(func, initValue):配列の2つの要素(左から右)に対し関数を実行し、単一の値を返す。
引数 func(function) : 各要素に実行する関数
 prevValue : 前回の関数実行結果の値
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 initValue : 最初の関数実行時の引数prevValueに代入される値(任意)
戻り値(return) : 実行される関数の最後の値
構文

let total = [1,2,3,4].reduce(function(prevValue, value, index, arr){
	return prevValue + value;
});

メソッドreduceの引数の仕組みは少々複雑です。まずは、次のコードを見てみます。

sample.js
const data = [100,200,300,400,500,600,700,800,900];
function test(prevValue, value, index){
	console.log(index, prevValue, value);
	return prevValue + value;
};
let total = data.reduce(test);
console.log( 'total : ', total );

コンソール画面

1 100 200
2 300 300
3 600 400
4 1000 500
5 1500 600
6 2100 700
7 2800 800
8 3600 900
total :  4500

メソッドreduceの第1引数には、各要素に対し実行する関数testを設定しており、第2引数には何も設定していません。
その場合、初回の関数testの実行では、配列のインデックス0の要素(100)が引数prevValueに代入され、インデックス1の要素(200)が引数valueに代入されます。
また、注目してほしいのは初回の実行がインデックス1からになっている点です。これはインデックス0とインデックス1の要素が同じ処理で実行されているためです。
2回目(インデックス2)以降、引数prevValueには前の実行結果(returnの値)が代入されることになります。関数testでは、引数prevValueと引数valueを足した数値をreturnしていますので、例えば5回目(インデックス5)の実行の際は、引数prevValueには前回の実行1000+500=1500の値が代入されます。
メソッドreduceの戻り値は、関数testの最後の実行の返り値が代入されますので、3600+900で4500が代入されることになります。これを表にすると次のようになります。

インデックス prevValue value 戻り値(return)
1回目の実行 1 100 (インデックス0の要素) 200 (インデックス1の要素) 300 (prevValue+value)
2回目の実行 2 300 (前回の戻り値) 300 (インデックス2の要素) 600 (prevValue+value)
3回目の実行 3 600 (前回の戻り値) 400 (インデックス3の要素) 1000 (prevValue+value)
4回目の実行 4 1000 (前回の戻り値) 500 (インデックス4の要素) 1500 (prevValue+value)
5回目の実行 5 1500 (前回の戻り値) 600 (インデックス5の要素) 2100 (prevValue+value)
6回目の実行 6 2100 (前回の戻り値) 700 (インデックス6の要素) 2800 (prevValue+value)
7回目の実行 7 2800 (前回の戻り値) 800 (インデックス7の要素) 3600 (prevValue+value)
8回目の実行 8 3600 (前回の戻り値) 900 (インデックス8の要素) 4500 (prevValue+value)

続いて、メソッドreduceの第2引数を指定した場合の結果を見てみます。

sample.js
const data = [100,200,300,400,500,600,700,800,900];
function test(prevValue, value, index){
	console.log(index, prevValue, value);
	return prevValue + value;
};
let total = data.reduce(test, 1000);
console.log( 'total : ', total );

コンソール画面

0 1000 100
1 1100 200
2 1300 300
3 1600 400
4 2000 500
5 2500 600
6 3100 700
7 3800 800
8 4600 900
total :  5500

メソッドreduceの第2引数に1000の値を指定しました。これで初回の引数prevValueには1000が代入されます。ループ処理も配列の長さ分行われます。以降の処理は先ほどのコードと同様です。
表にまとめてみます。

インデックス prevValue value 戻り値(return)
1回目の実行 0 1000 (メソッドreduceの第2引数の値) 100 (インデックス0の要素) 1100 (prevValue+value)
2回目の実行 1 1100 (前回の戻り値) 200 (インデックス1の要素) 1300 (prevValue+value)
3回目の実行 2 1300 (前回の戻り値) 300 (インデックス2の要素) 1600 (prevValue+value)
4回目の実行 3 1600 (前回の戻り値) 400 (インデックス3の要素) 2000 (prevValue+value)
5回目の実行 4 2000 (前回の戻り値) 500 (インデックス4の要素) 2500 (prevValue+value)
6回目の実行 5 2500 (前回の戻り値) 600 (インデックス5の要素) 3100 (prevValue+value)
7回目の実行 6 3100 (前回の戻り値) 700 (インデックス6の要素) 3800 (prevValue+value)
8回目の実行 7 3800 (前回の戻り値) 800 (インデックス7の要素) 4600 (prevValue+value)
9回目の実行 8 4600 (前回の戻り値) 900 (インデックス8の要素) 5500 (prevValue+value)

続いて、メソッドreduceRightについて解説します。

メソッドreduceRight

array.reduceRight(func, initValue):配列の2つの要素(右から左)に対し関数を実行し、単一の値を返す。
引数 func(function) : 各要素に実行する関数
 prevValue : 前回の関数実行結果の値
 value : 配列の要素
 index(number) : インデックス
 array(array) : 対象配列
引数 initValue : 最初の関数実行時の引数prevValueに代入される値(任意)
戻り値(return) : 実行される関数の最後の値
構文

let total = [1,2,3,4].reduceRight(function(prevValue, value, index, arr){
	return prevValue + value;
});

メソッドreduceRightは、メソッドreduceと似ています。メソッドreduceは配列の要素の左から順に処理の対象にしていますが、メソッドreduceRightはその逆で、配列の要素の右から(後ろから)順に対象にしていきます。メソッドreduceの解説で使用したコードをそのままメソッドreduceRightにして見てみます。

sample.js
const data = [100,200,300,400,500,600,700,800,900];
function test(prevValue, value, index){
	console.log(index, prevValue, value);
	return prevValue + value;
};
let total = data.reduceRight(test);
console.log( 'total : ', total );

コンソール画面

7 900 800
6 1700 700
5 2400 600
4 3000 500
3 3500 400
2 3900 300
1 4200 200
0 4400 100
total :  4500

関数testは、引数prevValueと引数valueを加算した値を戻り値としているので、変数totalに代入される結果はメソッドreduceと同じになります。ただし、その過程のループ処理の実行順がメソッドreduceと逆になっていることがわかります。

インデックス prevValue value 戻り値(return)
1回目の実行 8 900 (インデックス8の要素) 800 (インデックス7の要素) 1700 (prevValue+value)
2回目の実行 7 1700 (前回の戻り値) 700 (インデックス6の要素) 2400 (prevValue+value)
3回目の実行 6 2400 (前回の戻り値) 600 (インデックス5の要素) 3000 (prevValue+value)
4回目の実行 5 3000 (前回の戻り値) 500 (インデックス4の要素) 3500 (prevValue+value)
5回目の実行 4 3500 (前回の戻り値) 400 (インデックス3の要素) 3900 (prevValue+value)
6回目の実行 3 3900 (前回の戻り値) 300 (インデックス2の要素) 4200 (prevValue+value)
7回目の実行 2 4200 (前回の戻り値) 200 (インデックス1の要素) 4400 (prevValue+value)
8回目の実行 1 4400 (前回の戻り値) 100 (インデックス0の要素) 4500 (prevValue+value)

メソッドreduceと同様に、メソッドreduceRightの第2引数を指定し場合も見てみます。

sample.js
const data = [100,200,300,400,500,600,700,800,900];
function test(prevValue, value, index){
	console.log(index, prevValue, value);
	return prevValue + value;
};
let total = data.reduceRight(test,1000);
console.log( 'total : ', total );

コンソール画面

8 1000 900
7 1900 800
6 2700 700
5 3400 600
4 4000 500
3 4500 400
2 4900 300
1 5200 200
0 5400 100
total :  5500

インデックス prevValue value 戻り値(return)
1回目の実行 8 1000 (メソッドreduceの第2引数の値) 900 (インデックス8の要素) 1900 (prevValue+value)
2回目の実行 7 1900 (前回の戻り値) 800 (インデックス7の要素) 2700 (prevValue+value)
3回目の実行 6 2700 (前回の戻り値) 700 (インデックス6の要素) 3400 (prevValue+value)
4回目の実行 5 3400 (前回の戻り値) 600 (インデックス5の要素) 4000 (prevValue+value)
5回目の実行 4 4000 (前回の戻り値) 500 (インデックス4の要素) 4500 (prevValue+value)
6回目の実行 3 4500 (前回の戻り値) 400 (インデックス3の要素) 4900 (prevValue+value)
7回目の実行 2 4900 (前回の戻り値) 300 (インデックス2の要素) 5200 (prevValue+value)
8回目の実行 1 5200 (前回の戻り値) 200 (インデックス1の要素) 5400 (prevValue+value)
9回目の実行 0 5400 (前回の戻り値) 100 (インデックス0の要素) 5500 (prevValue+value)

以上が、Arrayオブジェクトで定義されている配列の要素をループし、各要素ごとに関数を実行するメソッドの解説になります。