web-dev-qa-db-ja.com

オブジェクトの別の配列によるオブジェクトの配列のフィルター

オブジェクトの配列を別のオブジェクトの配列でフィルタリングしたい。

このようなオブジェクトの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' } },
];

arrayanotherArrayをフィルターし、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,
      });
    }
  }
6
arif

前述の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);
4
kind user

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)
4
Akrion

では、この手順を段階的に解決しましょう。

プロセスを単純化するために、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]
})

この方法では、最初にマップを作成するために少し時間を「浪費」しますが、その後、より迅速に要素を見つけることができます。

ここからさらに最適化を行うことができますが、この質問にはこれで十分だと思います

1

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));
0
Adrian Brand