【问题标题】:Looping through Array or Object to update counts循环遍历数组或对象以更新计数
【发布时间】:2020-01-07 17:45:36
【问题描述】:

我有一个类似以下的问题:

let fruits = { Apples:0, Bananas:0, Oranges:0 }


var basket = ['Apples', 'Oranges', 'Bananas', 'Potatoes', 'Cucumber']

目标:根据篮子数组中水果的存在来更新水果对象中的计数。

注意:fruits 对象可能包含比篮子数组中的项目更多的属性(水果)。

对于这种情况,请假设购物篮中的商品不会出现多次。

我想出了两个解决方案:

for (const fruit of Object.keys(fruits)) {
    basket.includes(fruit) && fruits[fruit]++
} 

basket.forEach( (fruit) => { 
    fruits.hasOwnProperty(fruit) && fruits[fruit]++ 
} )

解决此问题的最有效方法(就性能而言)是什么?

【问题讨论】:

  • 如果有多个香蕉,第一个甚至都行不通,对吧?
  • 我假设如果 'basket' 有两次香蕉,你会希望 fruitsBananas: 2
  • 对不起,我应该澄清一下 - 在这种情况下,您不太可能让任何项目出现两次。
  • 不太可能!= 从不

标签: javascript arrays loops object


【解决方案1】:

如果你只问表演,第二种方式更好,这就是为什么:

在第一种情况下,您正在遍历键数组,并且对于每个键,您都遍历整个 basket。这会将订单带到O(n * m),其中n 是键的数量,m 是篮子中水果的数量。

您可以将第二个解决方案重写为fruit in fruits && fruits[fruit]++,结果是对于basket 中的每个水果,您只需要检查对象中是否存在键,这几乎会在恒定时间内发生,因此订单将是O(m)

【讨论】:

  • 我不同意,两种解决方案的时间复杂度相同。在第一个中,来自 fruits 对象的每个键都与 basket 数组中的每个水果进行检查。因此,时间复杂度:O(mn)。在第二个中,basket 数组中的每个水果都针对 fruits 对象的每个键进行检查。这又是 O(mn).
  • 我认为@hrithik 是正确的。检查对象是否具有值与数组是否具有值有任何性能差异吗? (检查水果对象是否有水果项目(来自篮子数组)与篮子数组是否有水果(来自水果对象的键)有什么区别?
  • @Adnan 我认为如果 fruits 使用 Javascript Maps 而不是 Object,那么搜索 a键入 fruits 将归结为 O(1)。这使得第二个解决方案*(with forEach)* O(m),其中,mbasket 数组的大小。跨度>
  • @hrithikgautham 谢谢你,直到现在我才知道地图。将进一步调查。
  • @hrithkgautham 根据stackoverflow.com/questions/368280/… JS 对象被实现为hashmaps,hashmaps 中的键检索具有恒定的时间复杂度,即O(1)。所以我说的是对的。此外,使用 for 而不是 foreach 变化不大,而使用第二个解决方案会在使用大量密钥时显着加快速度。你可以检查jsperf.com/object-faster-loop这个看看我的答案是否正确。
【解决方案2】:

专家说主流for loop最适合迭代。与forEachfor of 循环相比,它们提供了良好的性能。

在底层,java 脚本引擎使用 for loop 处理 forEachfor of 循环。

我将提供一个使用for loop 的示例来解决这个问题,

let fruits = { Apples:0, Bananas:0, Oranges:0 }


var basket = ['Apples', 'Oranges', 'Bananas', 'Potatoes', 'Cucumber']

// using for loop
for(let i = 0 ; i < basket.length ; i++){
  const fruitFromBasket = basket[i];
  if(fruitFromBasket in fruits)
    fruits[fruitFromBasket]++;
}
console.log("fruits: ", fruits);

总而言之,如果您关心性能,那么for loop 是您的最佳选择。

这里有几个与此讨论相关的链接,
stackoverflow.
hackernoon.
davidtang.

【讨论】:

    【解决方案3】:

    您可以通过在frequency 函数中传递fruits 对象作为第二个可选参数来指定是要保留所有结果,还是只保留您想要的结果。

    frequency 构建所有发现的水果的频率图。

    如果您想要真正的优化,请在Code Review Stack Exchange 网站上询问。

    let basket = ['Apples', 'Oranges', 'Bananas', 'Potatoes', 'Cucumber']
    let fruits = { Apples: 0, Bananas: 0, Oranges: 0 }
    
    console.log(frequency(basket, fruits)) // only the ones request
    console.log(frequency(basket))         // all fruits
    
    function frequency(items, initial) {
      return items.reduce((freq, item, index) => {
        return initial == null || (initial != null && initial[item] != null)
          ? Object.assign(freq, { [item] : (freq[item] || 0) + 1 })
          : freq
      }, Object.assign({}, initial))
    }
    .as-console-wrapper {
      top: 0;
      max-height: 100% !important;
    }

    反馈

    至于你想问的问题,你可以从这两个方面来看。

    1. 遍历篮子中的所有物品并增加水果计数

    这将不得不一直遍历篮子,但它是有效的,因为它是 O(n) + O(1)。

    basket.forEach(fruit => {
      if (fruits[fruit] !== undefined) {
        fruits[fruit]++
      }
    })
    console.log(fruits)
    
    1. 循环检查水果并检查它们是否在篮子中。

    这是你想要的行为,因为你只检查你想知道的水果,水果数量永远不会超过 1。

    Object.keys(fruits).forEach(fruit => {
      if (basket.includes(fruit)) {
        fruits[fruit]++;
      }
    })
    console.log(fruits);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 2020-07-19
      • 1970-01-01
      • 2019-12-17
      • 2016-07-28
      相关资源
      最近更新 更多