【问题标题】:filter array of objects by another array of objects通过另一个对象数组过滤对象数组
【发布时间】:2019-05-05 07:05:12
【问题描述】:

我想用另一个对象数组过滤对象数组。

我有 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' } },
];

我想通过anotherArray 过滤array 并返回 anotherArray 中不存在且具有子项的项目。

所以我想要的输出是:

[ { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } ]

注意:我已经使用 for 循环完成了此操作,但运行速度太慢。我想使用数组过滤方法来做到这一点

我的 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,
      });
    }
  }

【问题讨论】:

标签: javascript arrays filter


【解决方案1】:

优化版本:不需要每次都搜索元素(find),创建字典并在 O(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 dict = anotherArray.reduce((acc, curr) => {
  const { id } = curr;
  acc[id] = curr;
  return acc;
}, {});

const result = array.filter((obj) => {
  const search = dict[obj.id];
  if (!search && obj.sub) return true;
  return false;
});

console.log(result);

【讨论】:

    【解决方案2】:

    您可以使用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)

    【讨论】:

      【解决方案3】:

      好,我们一步步解决这个问题。

      为了简化这个过程,我们假设两个元素可以被认为是相等的,如果它们都具有相同的id。

      我将使用的第一种方法是迭代第一个数组,然后对于每个元素,迭代第二个数组以检查您在上面定义的条件。

      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]
      })
      

      这样一开始你“浪费”了一点时间来创建你的地图,但是你可以更快地找到你的元素。

      从这里可能会有更多的优化,但我认为这对这个问题来说已经足够了

      【讨论】:

        【解决方案4】:

        您可以使用 JSON.stringify 来比较这两个对象。最好写一个递归比较对象所有属性的函数。

        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));

        【讨论】:

          【解决方案5】:

          就像 Felix 提到的那样,Array#filter 不会比原生 for 循环运行得更快,但是如果你真的希望它作为函数式的方式,这里有一个可能的解决方案:

          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);

          【讨论】:

          • 哦,谢谢你,效果很好!我的数组中有 300 多个项目,这就是为什么我认为 for 循环运行缓慢
          • 谢谢,这正是我想要的,稍作修改。我有 2 个数组,需要在第三个数组中获得它们的差异。我刚刚删除了 && 子部分。 +1
          猜你喜欢
          • 2019-07-18
          • 2021-10-30
          • 2020-01-27
          • 1970-01-01
          • 1970-01-01
          • 2018-10-03
          • 2021-05-14
          相关资源
          最近更新 更多