配列・連想配列

【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()で確認して、存在しなかったら初期値の配列に追加します。
あとは、この繰り返しです。

reduce()については、次のページを参照してください。
【JavaScript】reduce()の使い方と10の使用例

関数を使用してみます。

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 :
    truearray1にマージする
    false → 新規配列にマージする(規定値)
  • isArray1AllowDuplicate :
    truearray1内の重複を削除しない
    falsearray1内の重複を削除する(規定値)

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]

上記コードのarray1array2は値が変更されている可能性があるので、毎回src1src2を複製しています。

 

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

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。