【问题标题】:How can I sort and operate on unique key values如何对唯一键值进行排序和操作
【发布时间】:2022-10-25 02:10:40
【问题描述】:

我想迭代一系列不同的评级,按每个唯一的 id 排序,求和并计算每个 id 评级的平均值。然后将平均值保存在一个新数组中,我可以在其中调用类似 averageRating[i] 的东西,其中每个条目将是每个 id 的评分。

原始对象数组如下所示,其中 id 可以是任意数字。

data = [{id: 1, rating: 1}, {id: 1, rating: 3}, {id: 1, rating: 1}, {id: 1, rating: 4}, {id: 1, rating}, {id: 2, rating: 3}, {id: 3, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5, {id: 1, rating: 1}, {id: 2, rating: 4}, {id: 1, rating: 3}, {id: 1, rating: 2}]

我只用一个特定的 id 就可以完成这项工作,执行如下操作,但是在弄清楚如何处理动态数量的 id 时遇到了一些麻烦。

var [average, updateAverage] = useState(0);

let ratings = data.map((item) => item.rating);

// Calculate average of the array
let sum = ratings.reduce((a, b) => a + b, 0);
let avg = sum / ratings.length || 0;
let avgRounded = Math.round(avg); // Round to nearest whole number

updateAverage = avgRounded;

【问题讨论】:

  • 在 map 函数中,您可以使用索引,也可以使用该索引。这就是所有数据将是唯一的

标签: javascript reactjs


【解决方案1】:

这可能超过了最高性能,但如果你喜欢一点数学,你实际上可以在一个循环中完成它。见this mathematical solution

const avgCount = {} // This is a temp var needed for accumulative averaging mathematical formula

// THIS VAR HAS THE ANSWER IN
const averages = data.reduce((averages, individualRating) => {
    // We need a counter of the number of times this ID has been seen already. This is needed by the algorithm.
    // Now we have seen a new rating with this id, increase the counter to reflect that.
    // If it's not there we start from 1.
    avgCount[individualRating.id] =  (avgCount[individualRating.id] ?? 0) + 1

    // Get the current rolling average. If there isn't one (this is first rating with this id we have seen), then its just the rating of this current item.
    const currAccumulatedAverage = averages[individualRating.id] ?? individualRating.rating

    // Calculate the new rolling average from the previous one
    averages[individualRating.id] = ((currAccumulatedAverage * (avgCount[individualRating.id] - 1)) + individualRating.rating) / avgCount[individualRating.id]
    
    return averages
}, {}) 

这应该是高性能的,因为没有多个循环或中间结构。

对于此输入:

let data = [{id: 1, rating: 1}, {id: 1, rating: 3}, {id: 1, rating: 1}, {id: 1, rating: 4}, {id: 1, rating: 1}, {id: 2, rating: 3}, {id: 3, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 5}, {id: 1, rating: 1}, {id: 2, rating: 4}, {id: 1, rating: 3}, {id: 1, rating: 2}]

它返回

{1: 2.8181818181818183, 2: 3.5, 3: 5}

【讨论】:

  • 糟糕,出错了,让我修复,给我几分钟
  • 现在全部排序,最初搞砸了算法:D
  • 它给了我 {1:11, 2:2, 3:1}
  • @Miller42 抱歉,是的,我更新了,你试过最新的吗?
  • 无需更改,但我刚刚更新了一个更干净的版本。虚荣的理由真的——它的逻辑是一样的。
【解决方案2】:

您可以使用常规循环并进行所有处理。读取内联 cmets:

// Rating data
const data = [
  {id: 1, rating: 1},
  {id: 1, rating: 3},
  {id: 1, rating: 1},
  {id: 1, rating: 4},
  {id: 1, rating: 2},
  {id: 2, rating: 3},
  {id: 3, rating: 5},
  {id: 1, rating: 5},
  {id: 1, rating: 5},
  {id: 1, rating: 5},
  {id: 1, rating: 1},
  {id: 2, rating: 4},
  {id: 1, rating: 3},
  {id: 1, rating: 2}
];

// Create new object for id -> average ratings
const avr = {};

// Iterate data array
for(const item of data) {
  // Create object of arrays
  // for calculate avarage later
  if(avr[item.id]) avr[item.id].push(item.rating);
  else avr[item.id] = [item.rating];
}

// Iterate object and calculate average values
for(const el in avr) {
  // Reduce
  const sum = avr[el].reduce((a, b) => a + b, 0);
  // Calculate average and update object
  avr[el] = Math.round(sum / avr[el].length || 0);
}

// Test
console.log(avr);

【讨论】:

  • 使用三元的副作用是如此蹩脚。
  • @RoboRobok 好的,专门为你更新了答案)
  • 非常感谢,效果很好!但是,如果可能,我会避免使用 for 循环!
猜你喜欢
  • 1970-01-01
  • 2012-07-02
  • 2016-11-17
  • 2019-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-01
  • 1970-01-01
相关资源
最近更新 更多