【JavaScript】二つの配列を要素を重複させずにマージする
更新日:2024/02/27
JavaScriptで、二つの配列を要素を重複させずに結合(マージ)する方法を紹介します。
総当たり:reduce()とindexOf()を使用
一方の配列に対して、もう一方の配列の要素が存在するかを一つずつ確認していきます。
単純なパターン
最初は単純なパターンを紹介します。
const mergeArrayNonDuplicate =
(array1,array2) =>
[...array1,...array2].reduce(
(result,e)=>{
if( result.indexOf(e) < 0 ) result.push( e );
return result;
},[]
);
まずは二つの配列をスプレッド構文([...array1,...array2])でまとめます。
■【JavaScript】 コード中の「…」は意味があった スプレッド/レスト構文
次にreduce()で、重複チェックをおこないます。
初期値として空配列([])を与ます。
コールバック関数は、初期値の配列に値が既に存在するかどうかをindexOf()で確認して、存在しなかったら初期値の配列に追加します。
あとは、この繰り返しです。
関数を使用してみます。
const array1 = [1,2,3,3,4,5],array2 = [2,4,6,6,8];
console.log( mergeArrayNonDuplicate( array1 , array2 ) );
// (7) [1, 2, 3, 4, 5, 6, 8]
重複させずに、マージできていますね。
複雑なパターン
今度は、少し複雑にしてみます。
次の条件を追加します。
- 結果をarray1に上書きするかどうかを指定可能にする
- array1に元から重複があるとき、そのままにするかどうかを指定可能にする
次のようなコードになります。
const mergeArrayNonDuplicate2 =
(array1,array2,isArray1Overwrite=false,isArray1AllowDuplicate=false) =>
(isArray1AllowDuplicate ? array2 : [...array1,...array2] ).reduce(
(result,e)=>{
if( result.indexOf(e) < 0 ) result.push( e );
return result;
},
isArray1AllowDuplicate
? (isArray1Overwrite ? array1 : Array.from( array1 ))
: (isArray1Overwrite ? (array1.length =0,array1) :[])
);
ここではmergeArrayNonDuplicate2()という名前で関数を作成しています。
この関数は2つの配列の他に引数を2つ受け取ります。
- isArray1Overwrite :
・true → array1にマージする
・false → 新規配列にマージする(規定値) - isArray1AllowDuplicate :
・true → array1内の重複を削除しない
・false → array1内の重複を削除する(規定値)
array1の重複を許容する場合は、reduce()の対象をarray2にして、初期値をarray1にします。
そうすることで、array1の内容はそのままの状態で、array2を追加していく形式になります。
reduce()の初期値は、少し複雑です。
次の表のようになります。
array1の重複 | array1の上書き | 初期値 |
---|---|---|
許容する | する | array1 |
しない | array1の複製 | |
許容しない | する | 値をクリアしたarray1 |
しない | 空の配列 |
関数を実行すると、次のようになります。
const src1 = [1,2,3,3,4,5],src2 = [2,4,6,6,8];
// 重複なし、上書きなし
let array1 = Array.from(src1),array2 = Array.from(src2);
console.log( mergeArrayNonDuplicate2( array1 , array2 ) , array1 );
// (7) [1, 2, 3, 4, 5, 6, 8] , (6) [1, 2, 3, 3, 4, 5]
// 重複なし、上書きあり
array1 = Array.from(src1),array2 = Array.from(src2);
console.log( mergeArrayNonDuplicate2( array1 , array2 ,true ) , array1 );
// [1, 2, 3, 4, 5, 6, 8] (7) [1, 2, 3, 4, 5, 6, 8]
// 重複あり、上書きなし
array1 = Array.from(src1),array2 = Array.from(src2);
console.log( mergeArrayNonDuplicate2( array1 , array2 ,false , true ) , array1 );
// [1, 2, 3, 3, 4, 5, 6, 8] (6) [1, 2, 3, 3, 4, 5]
// 重複あり、上書きあり
array1 = Array.from(src1),array2 = Array.from(src2);
console.log( mergeArrayNonDuplicate2( array1 , array2 ,true , true ) , array1 );
// [1, 2, 3, 3, 4, 5, 6, 8] (8) [1, 2, 3, 3, 4, 5, 6, 8]
上記コードのarray1、array2は値が変更されている可能性があるので、毎回src1、src2を複製しています。
Setオブジェクトを使用する
Setオブジェクトは配列のように値を格納できるオブジェクトです。
配列と異なる点は、インデックスでアクセスできない点と、同じ値は一つのみ格納できる点です。
この二つ目の特性を活かすことで、二つの配列を重複させずにマージできます。
Setオブジェクトを使って、二つの配列を重複させずにマージする関数を作成します。
const mergeArrayNonDuplicate3 = (array1,array2) =>
Array.from(new Set([ ...array1 , ...array2 ]));
Setオブジェクトは、インスタンス作成時に配列(イテラブルなオブジェクト)を受け取ります。
そのため、スプレッド構文で配列を生成しています。
前項でarray1の上書きを行いましたが、上記の方法は上書きできません。
また、array1が元から重複していたとき、そのまま残すような指定もできません。
しかし処理効率はこちらの方が良いので、単純なマージならSetオブジェクトを使用した方がよいです。
作成した関数を使用してみます。
const src1 = [1,2,3,3,4,5],src2 = [2,4,6,6,8];
console.log( mergeArrayNonDuplicate3( array1 , array2 ) );
// (7) [1, 2, 3, 4, 5, 6, 8]
重複データを除いてマージできていますね。
要素がオブジェクトの時の重複排除マージ
要素がオブジェクトで、nameやidで重複確認を行うケースがあります。
この場合は、次のように関数を作成します。
const mergeArrayNonDuplicate4 =
(array1,array2,propName) =>
[...array1,...array2].reduce(
(result,e)=>{
const checkValue = e[propName];
if( result.checkArray.indexOf(checkValue) < 0 ) {
result.checkArray.push( checkValue );
result.r.push( e );
}
return result;
},{checkArray:[],r:[]}
).r;
キーとなる値を配列に格納しておき、その配列にプロパティ値が含まれていないかをチェックします。
同時に処理中のオブジェクトを、別の配列に格納します。
最後に、reduce()の結果からオブジェクトが格納された配列のみを返します。
関数をテストしてみます。
const objArray1 = [{id:1,name:"a"},{id:2,name:"b"}];
const objArray2 = [{id:2,name:"b"},{id:3,name:"c"}];
console.log( mergeArrayNonDuplicate4( objArray1 , objArray2 , "id" ) );
// (3) [{id:1,name:"a"},{id:2,name:"b"},{id:3,name:"c"}]
想定通りの結果になりました。
更新日:2024/02/27
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。