オブジェクトの配列を別のオブジェクトの配列でフィルタリングしたい。
このようなオブジェクトの2つの配列があります。
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
array
でanotherArray
をフィルターし、anotherArrayに存在せず、subを持つアイテムを返します。
したがって、私の望ましい出力は次のとおりです。
[ { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } ]
注:forループでこれを実行しましたが、動作が遅すぎます。Arraysフィルターメソッドを使用してこれを実行したい
Forループで使用しているコード:
for (let i = 0; i < array.length; i += 1) {
let exist = false;
const item = array[i];
for (let j = 0; j < anotherArray.length; j += 1) {
const anotherItem = anotherArray[j];
if (item.id === anotherItem.id) {
exist = true;
}
}
if (item.sub && !exist) {
this.newArray.Push({
text: `${item.sub.name} / ${item.name}`,
value: item.id,
});
}
}
前述のFelixのように、Array#filter
は、ネイティブのforループよりも速く動作しませんが、機能的な方法として本当に必要な場合は、次の1つの解決策があります。
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const r = array.filter((elem) => !anotherArray.find(({ id }) => elem.id === id) && elem.sub);
console.log(r);
Array.filter
を使用してから、 Array.some
を使用できます。 Array.find
は:
const a1 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } }, { id: 4, name: 'a4', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const a2 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const result = a1.filter(({id, sub}) => !a2.some(x => x.id == id) && sub)
console.log(result)
では、この手順を段階的に解決しましょう。
プロセスを単純化するために、2つの要素が同じid
を持つ場合、2つの要素は等しいと見なすことができると仮定します。
私が使用する最初のアプローチは、最初の配列を反復し、各要素について、2番目の配列を反復して、上記で定義した条件を確認することです。
const A = [ /* ... */]
const B = [ /* ... */]
A.filter(el => {
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB && !!B.sub
})
AとBの要素が同じIDを持っているときに本当に同じであると確信している場合、sub
プロパティなしですべてのA要素をスキップして、少し上に実行することができます。
A.filter(el => {
if (!el.sub) return false
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB
})
さて、配列がそれよりも大きい場合、要素をBに探すのに多くの時間を無駄にしていることを意味します。通常、これらの場合、探している配列を次のようにマップに変換します。
var BMap = {}
B.forEach(el => {
BMap[el.id] = el
})
A.filter(el => {
if (!el.sub) return false
return !!BMap[el.id]
})
この方法では、最初にマップを作成するために少し時間を「浪費」しますが、その後、より迅速に要素を見つけることができます。
ここからさらに最適化を行うことができますが、この質問にはこれで十分だと思います
JSON.stringifyを使用して、2つのオブジェクトを比較できます。オブジェクトのすべてのプロパティを再帰的に比較する関数を作成することをお勧めします。
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const notIn = (array1, array2) => array1.filter(item1 => {
const item1Str = JSON.stringify(item1);
return !array2.find(item2 => item1Str === JSON.stringify(item2))
}
);
console.log(notIn(array, anotherArray));