JavaScriptを使っていると、配列の中のある特定の要素にのみ処理を実行したいというニーズが発生することがあります。
簡単な例でいうと、[1, 2, 3, 4, 5]という配列の2と4の前にだけ、新しい要素を追加するといった処理です。
この処理を行うためには、mapメソッド、reduceメソッド、concatメソッドを組み合わせる必要があります。
(通称map & reduceと呼ばれたりまもします)
mapメソッドとは何か?
mapメソッドとは、配列の中の要素を一つづつ取り出して、指定の処理を実行し、新たな配列をつくる非破壊処理です。
例えば、[1, 2, 3]という配列の各数値を2倍して[2, 4, 6]を作るといった処理ができます。
使い方は以下のようになります。
array.map( ( 引数1, 引数2, 引数3 ) => { 処理 } )
reduceメソッドとは何か?
reduceメソッドとは、配列の要素を一つずつ取り出し、指定した処理を行っていき、最終的に一つの値を返す関数です。
以下のように使います。
array.reduce( ( 引数1, 引数2, 引数3m 引数4 ), 初期値 => 処理 )
指定した配列やreduceの途中の処理結果など、どのデータを使いたいかによって指定する引数の数が異なります。
最大で4つの引数をとります。それぞれの引数には次の値が入ります。
引数 | 内容 |
---|---|
第1引数 | 処理前の値(直前の処理結果) |
第2引数 | 現在の要素の値 |
第3引数 | 現在の要素のインデックス番号 |
第4引数 | 元の配列 |
concatメソッドとは何か?
concatメソッドを使うと、対象となる元の配列はそのままで、指定した要素を追加した新たな配列を作成することができます。
配列と配列を結合して、一つの新しい配列を作る場合に使うことができます。
以下のように使います。
let 変数 = 配列.concat( 追加する要素 )
考え方|配列の中の特定の値にだけ処理を行う
まず、配列の中の特定の値にだけ処理を行うためにの考え方を解説します。
例えば、以下のような配列があったときに、
arr = [1, 2, 3, 4, 5]
値が2と4の場合だけ処理を加えて、前方に「a」という文字列を足す場合を考えます。
まずは、mapメソッドを使って、要素の値を一つ一つ取り出し、値が2と4のときだけ、前方に「a」を加えた配列を作ります。それ以外は対象の要素のみの配列にします。
[ [1], ["a", 2], [3], ["a", 4], [5] ]
現時点で2次元配列になっているので、reduceとconcatを使って1次元配列にします。
[1, "a", 2, 3, "a", 4, 5]
以上で完了です。
コードの書き方
mapの処理は以下のようになります。
res = arr.map(( x )=>{
if( 条件式 ){
return [ 処理1 ]
}else{
return [ x ]
}
})
この処理で2次元配列ができあがります。
これをreduceメソッドとconcatで1次元配列に戻します。
res = res.reduce( ( acc, elem )=>{
return acc.concat( elem )
})
実例
[1, 2, 3, 4, 5]という配列 arr に対して、値が2と4の場合だけ処理を加えて、前方に「a」という文字列を足す場合は以下のようになります。
//元の配列
arr = [1, 2, 3, 4, 5]
targetVals = [2, 4]
//挿入する値
addValue = "a"
//2次元配列にする
res = arr.map( ( elem ) =>{
if( targetVals.indexOf( elem ) !== -1 ){
return [ addValue, elem ]
}else{
return [ elem ]
}
})
console.log(res)
//[[1], ["a", 2], [3], ["a", 4], [5]]
//1次元配列に戻す
res = res.reduce( ( acc, elem )=>{
return acc.concat( elem )
})
console.log(res)
//[1, "a", 2, 3, "a", 4, 5]
if( targetVals.indexOf( x ) !== -1 )
indexOfメソッドは指定した要素が配列の中に含まれていれば、配列番号を返し、存在しない場合は「-1」を返します。
mapで取り出した各要素が、targetVals = [2, 4]
の中に含まれる場合は、[ addValue, elem ]
を実行し、なければ[elem]
で値を配列にして返します。
メソッドチェーンで記述する
メソッドチェンを使うと、mapの処理と、reduce(&concat)の処理をつなげることができます。
メソッドチェーンとは何か?
メソッドチェーンとはメソッドを「.(ドット)」でつなぐ処理です。
メソッド1.メソッド2.メソッド3....
書き方
mapの処理は以下のようになります。
res = arr.map(( x )=>{
if( 条件式 ){
return [ 処理1 ]
}else{
return [ x ]
}
}).reduce( ( acc, elem )=>{
return acc.concat( elem )
})
コードがかなりシンプルになります。
実例
//元の配列
arr = [1, 2, 3, 4, 5]
targetVals = [2, 4]
//挿入する値
addValue = "a"
//2次元配列にする
res = arr.map( ( elem ) =>{
if( targetVals.indexOf( elem ) !== -1 ){
return [ addValue, elem ]
}else{
return [ elem ]
}
}).reduce( ( acc, elem )=>{
return acc.concat( elem )
})
console.log(res)
//[1, "a", 2, 3, "a", 4, 5]
応用編:Setオブジェクトを使う
Setオブジェクトを使うと配列の重複を削除することができます。
例えば、対象となる要素の配列が[2,2,4,4,2]のように値が重複していた場合、Setオブジェクトにすると、{2,4}のようになり、重複した値を削除することができます。
Setオブジェクトに対してhasメソッドを使うと、指定した要素が存在する場合はtrueを、ない場合はfalseを返します。
Setオブジェクトと、map, reduce, concatを使うと以下のような処理を実行することができます。
//元の配列
arr = [1,2,3,4,5]
//前に要素を追加したい値(重複あり&複数)
targetVals = [2,2,4,4,2]
//挿入する値
addValue = "a"
//重複を削除する
setTarget = new Set( targetVals ) //{2,4}
//2次元配列にする
res = arr.map( ( elem ) => {
if( setTarget.has( elem )){
return [ addValue, elem ]
}else{
return [ elem ]
}
}).reduce( ( acc, elem )=>{
return acc.concat( elem )
})
console.log(res)
//[1, "a", 2, 3, "a", 4, 5]