【问题标题】:Summarize array of objects and calculate average value for each unique object name汇总对象数组并计算每个唯一对象名称的平均值
【发布时间】:2014-02-17 02:41:32
【问题描述】:

我有一个这样的数组:

var array = [
     {
       name: "a",
       value: 1 
     },
     {
       name: "a",
       value: 2 
     },
     {
       name: "a",
       value: 3 
     },
     {
       name: "b",
       value: 0 
     },
     {
       name: "b",
       value: 1 
     }
 ];

我需要一个这样的数组:

var newarray = [
     {
       name: "a",
       value: 2
     },
     {
       name: "b",
       value: 0.5
     }
 ]

其中新数组将每个唯一名称作为具有平均值的对象。

有没有简单的方法可以做到这一点?

【问题讨论】:

  • 你只想知道ab吗?
  • a 和 b 只是示例。我的数组将有更多的名称,而不仅仅是 a 和 b。我的价值观怎么不正确? a 有值 1、2 和 3。1+2+3 = 6,a 有 3 个值,因此 6/3 = 2。b 有值 0 和 1。0 + 1 = 1,有 2 个值b,所以 1/2 = 0.5 .
  • @user3317337:对不起,那是我的错误——我虽然第三个是“b”,而不是“a”。

标签: javascript arrays unique average


【解决方案1】:

您必须遍历数组,计算每个对象的总和和计数。这是一个快速实现:

function average(arr) {
    var sums = {}, counts = {}, results = [], name;
    for (var i = 0; i < arr.length; i++) {
        name = arr[i].name;
        if (!(name in sums)) {
            sums[name] = 0;
            counts[name] = 0;
        }
        sums[name] += arr[i].value;
        counts[name]++;
    }

    for(name in sums) {
        results.push({ name: name, value: sums[name] / counts[name] });
    }
    return results;
}

Demonstration

注意,如果你使用像 Underscore.js 这样的库,这种事情会变得更容易:

var averages = _.chain(array)
                .groupBy('name')
                .map(function(g, k) {
                    return { 
                        name: k, 
                        value: _.chain(g)
                                .pluck('value')
                                .reduce(function(x, y) { return x + y })
                                .value() / g.length
                    };
                })
                .value();

Demonstration

【讨论】:

  • 我不知道下划线版本“容易得多”。需要了解chaingroupBymappluckvaluereduce plus 需要一个适合 reduce 的函数。我敢打赌它也比 POJS 版本慢很多(可以更简洁)。 ;-)
  • @RobG 公平点。如果您更喜欢函数式编程策略,那就更容易了。我主要是想提供一个替代方案。
【解决方案2】:
var array = [
     {
       name: "a",
       value: 1 
     },
     {
       name: "a",
       value: 2 
     },
     {
       name: "a",
       value: 3 
     },
     {
       name: "b",
       value: 0 
     },
     {
       name: "b",
       value: 1 
     }
 ];
var sum = {};
for(var i = 0; i < array.length; i++) {
    var ele = array[i];
    if (!sum[ele.name]) {
        sum[ele.name] = {};
        sum[ele.name]["sum"] = 0;
        sum[ele.name]["count"] = 0;
    }
    sum[ele.name]["sum"] += ele.value;
    sum[ele.name]["count"]++;
}
var result = [];
for (var name in sum) {
    result.push({name: name, value: sum[name]["sum"] / sum[name]["count"]});
}
console.log(result);

【讨论】:

    【解决方案3】:

    你可以用一行代码用Alasql库做到这一点:

    var newArray = alasql('SELECT name, AVG([value]) AS [value] FROM ? GROUP BY name',
                           [array]);
    

    这里我把“值”放在方括号中,因为 VALUE 是 SQL 中的关键字。

    在 jsFiddle 尝试this example

    【讨论】:

      【解决方案4】:

      这里是一个ES2015版本,使用reduce

       let arr = [
        { a: 1, b: 1 },
        { a: 2, b: 3 },
        { a: 6, b: 4 },
        { a: 2, b: 1 },
        { a: 8, b: 2 },
        { a: 0, b: 2 },
        { a: 4, b: 3 }
      ]
      
      arr.reduce((a, b, index, self) => {
       const keys = Object.keys(a)
       let c = {} 
      
       keys.map((key) => {
        c[key] = a[key] + b[key]
      
        if (index + 1 === self.length) {
          c[key] = c[key] / self.length
        }
       })
      
       return c
      })
      

      【讨论】:

        【解决方案5】:

        还有一个使用 ECMA5 的可能解决方案(我们似乎缺少一个)

        var sums = {},
            averages = Object.keys(array.reduce(function (previous, element) {
                if (previous.hasOwnProperty(element.name)) {
                    previous[element.name].value += element.value;
                    previous[element.name].count += 1;
                } else {
                    previous[element.name] = {
                        value: element.value,
                        count: 1
                    };
                }
        
                return previous;
            }, sums)).map(function (name) {
                return {
                    name: name,
                    average: this[name].value / this[name].count
                };
            }, sums);
        

        开启jsFiddle

        【讨论】:

          【解决方案6】:

          2020年10月,我认为这是最短的方式(ES6+)

          const getAveragesByGroup = (arr, key, val) => {
            const average = (a, b, i, self) => a + b[val] / self.length;
            return Object.values(
              arr.reduce((acc, elem, i, self) => (
                  (acc[elem[key]] = acc[elem[key]] || {
                    [key]: elem[key],
                    [val]: self.filter((x) => x[key] === elem[key]).reduce(average, 0),
                  }),acc),{})
            );
          };
          
          console.log(getAveragesByGroup(array, 'name', 'value'))
          

          自己试试吧:)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-02-07
            • 1970-01-01
            • 1970-01-01
            • 2013-02-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多