JavaScript で互いにオブジェクトを要素に持つ配列2つを比較して、重複していない要素のみを取り出す

経緯

JavaScript で、互いにオブジェクトを要素に持つ配列2つを比較して、重複していない要素のみを取り出す処理を試みたのでメモしておきます。

前提

前提として、次のような2つの配列があったとします。

  • 互いにオブジェクトを要素として持つ配列
  • 配列2は配列1の子集合(サブセット)

この2つの配列を比較して、配列1の中から配列2に含まれない要素のみの配列を作りたい、と考えました。

想定する最終結果も併せて付記しておきます。

配列1

[
    {
        "value": "value-0",
        "label": "Château d'If"
    },
    {
        "value": "value-1",
        "label": "Marseille"
    },
    {
        "value": "value-2",
        "label": "France"
    },
    {
        "value": "value-3",
        "label": "Le Comte de Monte-Cristo"
    },
    {
        "value": "value-4",
        "label": "Alexandre Dumas"
    },
    {
        "value": "value-5",
        "label": "Rhino"
    },
    {
        "value": "value-6",
        "label": "prison"
    },
    {
        "value": "value-7",
        "label": "François I"
    },
    {
        "value": "value-8",
        "label": "Jean-Baptiste Kléber"
    }
]

配列2

[
    {
        "value": "value-0",
        "label": "Château d'If"
    },
    {
        "value": "value-1",
        "label": "Le Comte de Monte-Cristo"
    },
    {
        "value": "value-2",
        "label": "Alexandre Dumas"
    }
]

得たい配列

[
    {
        "value": "value-1",
        "label": "Marseille"
    },
    {
        "value": "value-2",
        "label": "France"
    },
    {
        "value": "value-5",
        "label": "Rhino"
    },
    {
        "value": "value-6",
        "label": "prison"
    },
    {
        "value": "value-7",
        "label": "François I"
    },
    {
        "value": "value-8",
        "label": "Jean-Baptiste Kléber"
    }
]

「2つの配列で重複した要素を除去した配列を生成する」というのはいくつか記事を見かけました。

しかし、それらは1次元配列のサンプルばかりだった上に、今回はさらに「要素の中の labelキー の値で比較したい」という内容だったので、さらにハードルが上がりました。

コード

最終的にはこちらのコードをベースにして作りました。

サンプルは以下。

// 配列
const Ar1 = [
    // 略
];
const Ar2 = [
    // 略
];

// 重複要素を除去した配列を返す関数
const getArraysDiff = (array1, array2) => {
    // 引数の各々の配列から label のみの配列を生成
    const array1LabelArray = array1.map((itm) => {
        return itm.label;
    });
    const array2LabelArray = array2.map((itm) => {
        return itm.label;
    });
    // label のみの配列で比較
    const arr1 = [...new Set(array1LabelArray)];
    const arr2 = [...new Set(array2LabelArray)];
    return [...arr1, ...arr2].filter((val) => {
        return !arr1.includes(val) || !arr2.includes(val);
    });
};

// 上述関数で重複除去した label の配列を得る
const ChateuDiff = getArraysDiff(Ar1, Ar2);
// filter メソッドで、元配列 から該当する label が存在する要素を除去する
const enferChateuDiff = Ar1.filter((item) => {
    return ChateuDiff.includes(item.label);
});

ざっくりこのような処理で想定していた結果を得られました。

余談

何故このようなことをしようかと思ったかというと、 React SelectdefaultCalue で指定した選択済みの項目が選択候補にも上がってしまっていたので、除外しようとしたためでした。

が、そもそも React Select 側は選択済み項目を除外する機能を元々持っていたので上述の処理は不要ということが分かったため、今回のコードは未使用となりました。

ちなみに、この機能が働かなかった原因は API で取得した 値から 上述のような labelvalue のオブジェクトを生成するループ処理の際に、 value に ID の数値を振り方を間違えていたため、 React Select から「異なる値」として認識されてしまっていたためでした。

また、仮に今回の処理をかけたとしても、初期表示では上手く選択済み項目が除外されますが、選択済み項目を削除した場合は選択可能な項目として再度選択肢に復活させる必要があるため、かなり手間がかかることが想定されたためオミットしていたと思います。

……本当、 React Select 側に標準搭載されていて良かったです。

参考

本題

label のみの配列を作る

配列操作

JSON のフォーマット

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。