Emotion Wave Tech Blog

福岡にあるエモーションウェーブ株式会社のエンジニアが書いています。

【javascript】2つの連想配列を比較し、重複するものを返す

目次

javascript連想配列を比較し、重複するものがないか、調べたい

どうも、高島です。

javascriptで2つの配列があります。 両方とも存在するデータを取り出すには、どうすればいいでしょうか?

普通の配列を比較するサンプルは、ネット上に結構あるのですが、連想配列の例があまり無かったので、記事を書きました。

早速ですが、filterの中でfilterを呼べば、可能です。

const ary1 = [
  {"keyno":"A1","room":"101"},
  {"keyno":"A1","room":"102"},
  {"keyno":"B1","room":"201"},
  {"keyno":"C1","room":"301"},
  {"keyno":"D1","room":"401"},
];


const ary2 = [
  {"keyno":"A1","room":"101"}, 
  {"keyno":"C3","room":"301"},
  {"keyno":"D1","room":"401"},
  {"keyno":"E1","room":"501"},
];

// 重複有り
var reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 0);
        
console.log("両方とも存在するデータ");
console.log(reList);

結果はこうです。

両方とも存在するデータ
[
  { keyno: 'A1', room: '101' },
  { keyno: 'D1', room: '401' }
]

filter内はどんな動きなのか?

実際はどんな処理がされているのでしょうか? console.logを埋め込んで確認してみます。

// 内部の動作を確認してみる
ary1.filter(ary1row=>{
    console.log("ary1row.room=" + ary1row.room);
    ary2.filter(
        ary2row=>{
            console.log("    ary2row.room=" + ary2row.room);
        })
});

結果はこうなりました。

ary1row.room=101
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=102
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=201
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=301
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=401
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501

ary1をループして、さらにその中でary2をループしていることがわかります。 filterを使えば、ループを書かなくて済む、というわけです。

冒頭の重複を取り出す処理に話を戻します。

ary2.filter関数が、ary1rowとary2rowを比較し、一致する行を返してくれます。 この部分ですね。

ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room)

ary2.filterは、比較条件がtrueならば、データを返します。 もしfalseなら何も返しません。

データを返ってきたか否かは、lengthで判断します。

もうちょっとわかりやすく、returnを省略せずに、lengthでのtrue/false判定を分離して書いてみます。

var reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 0);

↓↓↓↓↓↓

var reList = ary1.filter(ary1row=>{
    var reAry2row = ary2.filter(
        ary2row=>{
            return (ary2row.keyno === ary1row.keyno &&
                    ary2row.room === ary1row.room)
        });

    return reAry2row.length > 0 ;
});

ary1rowとマッチしたreAry2row が存在すれば、length > 0 、ですのでtrueで返します。 ary1.filter()はtrueとなったary1rowをreListに返します。

こうして重複したデータのみが reListにセットされます。

2つの配列を比較し、重複しないものを返す

では、ary1に存在するがary2に存在しないデータは?

これは簡単です。結果が0件のものですね。 length === 0 に変えます。

// 重複しない
reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length === 0);

console.log("ary1に存在するがary2に存在しないデータ");
console.log(reList);
ary1に存在するがary2に存在しないデータ
[
  { keyno: 'A1', room: '102' },
  { keyno: 'B1', room: '201' },
  { keyno: 'C1', room: '301' }
]

(おまけ)自分自身の配列内で重複を取り出す

これまでは、異なる配列同士を比較しましたが、 今度は自分自身と比較すればいいです。

const ary1 = [
  {"keyno":"A1","room":"101"},
  {"keyno":"A1","room":"102"},
  {"keyno":"B1","room":"201"},
  {"keyno":"C1","room":"301"},
  {"keyno":"A1","room":"101"},
  {"keyno":"C1","room":"301"},
  {"keyno":"D1","room":"401"},
];


// 重複有り
var reList = ary1.filter((ary1row,ary1idx,selfary)=>selfary.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 1);
        
console.log("重複するデータ");
console.log(reList);
重複するデータ
[
  { keyno: 'A1', room: '101' },
  { keyno: 'C1', room: '301' },
  { keyno: 'A1', room: '101' },
  { keyno: 'C1', room: '301' }
]

filter() はcallback関数を引数にとります。 このcallback関数の第3引数は自分自身の配列です。 それと比較すればいいです。

まとめ

filter内でfilterを呼び、そこで比較すれば、二重ループを書く必要はなくなります。

最初にfilter見たときは訳わかりませんでしたが、理解するとスッキリ書けます。

欠点といえば、読むには慣れが必要なことでしょうか。

人が書いたものは、正直ちょっとわかりにくいことが多いです。

var reList = ary1.filter((x,y,z)=>z.filter(
    w=>
        w.keyno === x.keyno &&
        w.room === x.room).length > 1);

アロー関数は、こんな感じで略した変数名で書くことが多いので、変数名からは内容が推測できませんし、returnを省略して書かれると、初心者はコードが読めずに苦しんだりします。

もう慣れるしかないんでしょうけどね。