【问题标题】:Extracting unique values from an array of objects where value is a nested object in JavaScript从对象数组中提取唯一值,其中值是 JavaScript 中的嵌套对象
【发布时间】:2020-03-19 14:03:56
【问题描述】:

假设我在 JavaScript 中有以下对象数组:

const requests = [
  {
    id: 1,
    person: {
      id: 1
    }
  },
  {
    id: 2,
    person: {
      id: 1
    }
  },
  {
    id: 3,
    person: {
      id: 2
    }
  },
  {
    id: 4,
    person: {
      id: 3
    }
  },
  {
    id: 5,
    person: {
      id: 2
    }
  }
]

我在下面写的内容将遍历数组中的每个项目,然后创建一个仅包含 person 对象的新数组。

const requestsPeopleIds = []
for (const request of requests) {
  requestsPeopleIds.push(request.person.id)
}

然后我使用该新数组并使用Set 创建另一个新数组以删除重复的ids:

const uniquePeopleIds = Array.from(new Set(requestsPeopleIds))

最终结果如我所料:

console.log(uniquePeopleIds) // [1, 2, 3]

这些是提出请求的人的唯一ids。所以在5 请求中,这些请求是由3 人提出的。

必须有更有效的方法来做到这一点,所以我正在与您联系stack overflow JS 大师。

提前致谢。

【问题讨论】:

  • 定义“高效”。关于 CPU 周期、内存使用…… - 除非我们谈论的是数十万个请求,或者一些非常严格的内存限制,否则这是另一种可能没有用甚至没有必要的微优化案例。
  • 这几乎是你能得到的最有效的。这是一个O(n) 解决方案,最多超过输入三倍。如果您跳过制作最终数组并仅使用 Set 的迭代器,则可以减少一次迭代,并且在某些情况下可能会有一些更有效的迭代方法,但总体而言,从复杂性角度来看,它似乎和它一样好。
  • @VLAZ - 我不同意。它总是 O(n log n),因为您总是必须遍历原始数组中的每个项目,然后搜索您正在组成的唯一值集。
  • @mankowitz 但是没有搜索。添加到集合是O(1) 操作,对集合/数组的迭代是O(n)。这是在做迭代和加法,总共O(n)
  • 很抱歉误导了你。请看我更新的答案。看起来最快的解决方案可能会有所不同,并且取决于许多因素。

标签: javascript arrays unique


【解决方案1】:

我想你已经掌握了基础知识。这是一种收紧代码的方法:

var ids = new Set;
requests.forEach(i => ids.add(i.person.id));

【讨论】:

  • 为什么不只是var ids = new Set(ids.map(i => i.person.id));
  • @StepUp - 感谢性能测试,非常感谢 :)
【解决方案2】:

您也可以使用map 方法和传播语法... 来做到这一点。

const requests = [{"id":1,"person":{"id":1}},{"id":2,"person":{"id":1}},{"id":3,"person":{"id":2}},{"id":4,"person":{"id":3}},{"id":5,"person":{"id":2}}]
const result = [...new Set(requests.map(({ person: { id }}) => id))]
console.log(result)

【讨论】:

  • 这样更有效率吗?因为我怀疑它在性能方面大致相同。
  • 而这种方式更“有效”?代码行,因为您已将 for...of... + .push() 替换为 .map()?
【解决方案3】:

可以通过人的id做一个对象作为key,获取对象的key。

const requests = [{"id":1,"person":{"id":1}},{"id":2,"person":{"id":1}},{"id":3,"person":{"id":2}},{"id":4,"person":{"id":3}},{"id":5,"person":{"id":2}}]


// Take an empty object
const uniques = {};

// Iterate through the requests array and make person's id as a
// key of the object and put any value at this index (here I put 1).
requests.forEach(request => (uniques[request.person.id] = 1));

// Finally get the keys of the unique object.
console.log(Object.keys(uniques));

【讨论】:

  • 这比 Set 更有效吗?
  • 设定的答案很好。我不知道将新项目添加到 Set 的成本。但是这种方法需要 O(n) 次。我认为这很好。
  • OP 要求一种有效的方法。 OP的代码已经是O(n)
  • 在 O(n) 下没有机会降低时间复杂度。我想他想要的是代码风格的效率。
【解决方案4】:

我做了一些研究并推断出一些有趣的事实:

  1. 看起来当我们有非常多样化的数据和更大的数组时,Set 集合显示不是最好的结果。 Set 是非常优化的集合,但是,在我看来,它应该始终检查元素是否已经添加到 Set 中。这种检查将需要O(n) 的复杂性。但是我们可以使用简单的 JavaScript object。检查object 是否包含键是 O(1)。所以object 将比Set 拥有巨大的优势。

  2. foreach箭头函数很方便,但是简单的for循环更快。

  3. 添加console.log 使Set 成为最快的解决方案,但是,如果没有console.log,最快的解决方案是for 循环和object 的组合。

所以没有console.log() 的性能最高的代码如下所示:

const hashMap = {};
const uniques = [];
for (let index = 0; index < requests.length; index++) {  
  if (!hashMap.hasOwnProperty(requests[index].person.id)){
      hashMap[requests[index].person.id] = 1;
      uniques.push(requests[index].person.id);
  }
}

但是,console.log() 的最高性能代码看起来像这样(我无法理解它发生的原因。知道它为什么会发生真的很棒):

var ids = new Set;
requests.forEach(i => ids.add(i.person.id));
console.log(ids)

测试:

【讨论】:

  • 这比使用 Set 更高效吗?
  • @downvoter tp downvote 的原因是什么?知道会很有帮助。这将有助于改善我的答案
猜你喜欢
  • 2013-04-02
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2021-09-21
  • 2021-11-29
  • 2018-10-14
相关资源
最近更新 更多