【问题标题】:Sum all data in array of objects into new array of objects将对象数组中的所有数据汇总到新的对象数组中
【发布时间】:2019-02-27 22:50:54
【问题描述】:

我有一个如下所示的对象数组:

var data = [
    { costOfAirtickets: 2500, costOfHotel: 1200 },
    { costOfAirtickets: 1500, costOfHotel: 1000 },
]

我想对数组中的每个元素求和以产生一个这样的数组:

var result = [ { costOfAirtickets: 4000, costOfHotel: 2200 } ]

我使用了 map 和 reduce 函数,但我只能像这样对单个元素求和:

data.map(item => item.costOfAirtickets).reduce((prev, next) => prev + next); // 22

目前,这会产生一个单一的值,根据最初的解释,这不是我想要的。

有没有办法在 Javascript 中或可能使用 lodash 中做到这一点。

【问题讨论】:

  • 为什么要它返回一个数组?
  • 因为我想在角度视图中循环数组。
  • 但是你不需要任何循环来处理单个对象。
  • 你的意思是一个对象数组。
  • costOfHotel 应该是2200 我猜

标签: javascript arrays lodash


【解决方案1】:

另一种解决方案是使用 Map不是 Array.prototype.map),因为它有多个 notable differences compared to objects

var data = [{
  costOfAirtickets: 2500,
  costOfHotel: 1200
}, {
  costOfAirtickets: 1500,
  costOfHotel: 1000
}]

let sums = data.reduce((collection,rcd)=>{
  Object.entries(rcd).forEach(([key,value])=>{
      let sum = collection.get(key) || 0
      collection.set(key, sum + +value)
  })
  return collection
}, new Map())

console.log(...sums.entries())

说明

外循环

上面首先使用reduce 方法遍历您的data 数组。我将其中的每个对象称为记录——在代码中通过变量rcd 进行区分。

reduce 的每次迭代都返回一个值,该值作为第一个参数传递给循环的下一次迭代。在这种情况下,参数 collection 包含该参数,即您的总和集。

内循环

reduce 循环中,记录的每个键/值对都使用forEach 进行迭代。要获取键/值对,使用Object.entries 方法。使用数组解构这些参数可以直接分配给各自的变量,keyvalue

检索/设置值

与原始对象不同,Map 有自己的方法来使用get()set() 获取和设置其条目。因此,首先使用get() 检索先前的总和,如果未设置则默认为0,这就是|| 0 所做的。此时,您可以假设之前的总和至少为 0 或更大,并将当前键的值添加到它上面。

地图的替代品

如果你觉得Map有点笨拙,你也可以使用类似的对象,比如Set,它有很多相同的方法(get()除外),或者您也可以使用原始对象(即{})。

【讨论】:

    【解决方案2】:

    你不需要映射数组,你实际上只是在减少它里面的东西。

    const totalCostOfAirTickets: data.reduce((prev, next) => prev + next.costOfAirTickets, 0)
    const totalCostOfHotel: data.reduce((prev, next) => prev + next.costOfHotel, 0)
    const totals = [{totalCostOfAirTickets, totalCostOfHotel}]
    

    一口气,你可以做类似的事情

    const totals = data.reduce((prev, next) => { 
        prev.costOfAirTickets += next.costOfAirTickets; 
        prev.costOfHotel += next.costOfHotel; 
    }, {costOfAirTickets: 0, costOfHotel: 0})
    

    【讨论】:

      【解决方案3】:

      洛达什

      使用 lodash reduce_.mergeWith 这是一个单行:

      var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
      
      var result = _.reduce(data, (r,c) => _.mergeWith(r, c, (o = 0, s) => o + s), {})
      
      console.log(result)
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

      仅限 ES6

      如果您不想改变原始数组,您可以像这样使用 ES6 reduceObject.entriesforEach

      var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
      
      // One liner
      var result1 = data.reduce((r, c) =>
        !Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value) && r, {})
      
      // More readable
      var result2 = data.reduce((r, c) => {
        Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value)
        return r
      }, {})
      
      console.log(result1)
      console.log(result2)

      如果我们不关心变异初始 data 数组,那么我们可以有一个单一的解决方案:

      var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
      
      data.reduce((r, c) => !Object.entries(c).forEach(([key,value]) => r[key] += value) && r)
      
      console.log(data[0])

      更具可读性:

      var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
      
      data.reduce((r, c) => {
        Object.entries(c).forEach(([key, value]) => r[key] += value)
        return r
      })
      
      console.log(data[0])

      变异示例和非变异示例之间的唯一区别是reduce初始值(以及在变异时我们使用 0 索引作为总和的累加器这一事实)。在变异中没有initial value,而在其他中我们以空对象文字开始。

      如果您需要将结果专门作为一个数组,则返回 [data] 用于变异示例,[result] 用于纯示例。

      【讨论】:

        【解决方案4】:

        这是lodash 方法

        _(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 1)).value()
        

        它将通过所有唯一键求和。

        var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
        
        var res = _(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 0)).value();
        
        console.log(res);
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

        将结果包装到[...] 或在末尾使用.castArray(),然后再使用.value() 展开,以防您想要一个数组作为结果。

        【讨论】:

          【解决方案5】:

          我的简单回答是这样的;

          var data = [{costOfAirtickets: 2500, costOfHotel: 1200},
                      {costOfAirtickets: 1500, costOfHotel: 1000}],
              res  = data.reduce((p,c) => Object.entries(c).reduce((r,[k,v]) => (r[k]+=v, r), p));
          console.log(res);

          .reduce() 的使用注意事项:如果数组项和累加值是同一类型,您可以考虑不使用初始值作为累加器,并使用之前的 (p) 和当前的 (@ 987654324@) 个元素。上面 sn-p 中的外归约就是这种类型。但是内部 reduce 需要一个键 (k) 值 (v) 对数组作为 [k,v] 返回一个对象,因此初始值 (p) 是必不可少的。

          结果是作为对象的累加值。如果您需要将它放在一个数组中,那么只需将它放在一个数组中,例如[res]

          【讨论】:

            【解决方案6】:

            这是我能想到的最简单的方法

            var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
            var sum ={};
            for(var obj in data){
              for(var ele in data[obj]){
                if(!data[obj].hasOwnProperty(ele)) continue;
                  if(sum[ele] === undefined){
                    sum[ele] = data[obj][ele];
                  }else{
                    sum[ele] = sum[ele] + data[obj][ele];
                  }
              }
            
            }
            var arr = [sum];
            console.log(arr);
            

            【讨论】:

              【解决方案7】:

              使用for..in迭代对象,使用reduce迭代数组

              var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
              
              var result = [data.reduce((acc, n) => {
                for (var prop in n) {
                  if (acc.hasOwnProperty(prop)) acc[prop] += n[prop];
                  else acc[prop] = n[prop];
                }
                return acc;
              }, {})]
              console.log(result)

              【讨论】:

              • 这个解决方案很好,因为它允许每次旅行自动包含不同的属性 ({..., costOfRentalCar: 300})。但它仅适用于旅行中的only 属性是成本的情况。
              • 使用acc.hasOwnProperty(prop) 代替acc[prop]。这可以防止在某些情况下,您正在检查是否有 __toString 或其他东西。此外,使用acc[prop] += +n[prop] || 0; 进行求和。如果我将一个字符串传递给它,它只会连接。 +n[prop] 强制将其转换为整数。 || 0 删除了其他虚假值,例如 NaN。 acc[prop] = n[prop] 也是 acc[prop] = +n[prop] || 0
              • 这个答案已经过时了好几年。人们不再使用for...in + hasOwnProperty,而是使用for(const key of Object.keys(...))。也可以使用 lodash 将整个事情作为简单的单线来完成。
              • @IsmaelMiguel 更简单,if (prop in acc) …
              • @Bergi 这不是更好。这正是不应该做的。试试'toString' in {}。如果它返回true,那么您的代码可能会中断。 if(proc in acc) 检查对象 原型中是否存在属性。你想要的东西在这里。
              【解决方案8】:

              var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
              
              var result = [data.reduce((acc, n) => {
                for (var prop in n) {
                  if (acc[prop]) acc[prop] += n[prop];
                  else acc[prop] = n[prop];
                }
                return acc;
              }, {})]
              console.log(result)

              【讨论】:

                【解决方案9】:

                使用 Lodash 简化您的生活。

                const _ = require('lodash')
                let keys = ['costOfAirtickets', 'costOfHotel']; 
                let results = _.zipObject(keys, keys.map(key => _.sum( _.map(data, key))))
                ...
                { costOfAirtickets: 4000, costOfHotel: 2200 }
                

                解释:

                • _.sum(_.map(data, key)):生成每个数组的总和
                • _.zipObject: 用数组 sum 压缩结果
                • 使用keys.map() 作为_.map 的每个键的总和不保证顺序。

                文档:

                【讨论】:

                • 您的解决方案不能解决 OP 提出的问题,即如何对这样的对象数组求和:var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]
                • @DaveKalu:为什么? result 正好包含要求的值。你不是OP吗? IMO这是最好的答案。 @Roopak:您仍然可以稍微简化一下:_.sumBy(data, key)_(data).sumBy(key)
                • @DaveKalu:这确实解决了所提出的确切问题。这也是这个页面上最干净的答案,正是我要写的。注意keys可以定义为const keys = Object.keys(data);
                • 这可能是最好的方法之一,但它需要知道我们将执行求和的键。在这种情况下(假设)其他答案也可以优化(因为大多数答案都是针对所有独特属性的总和)。知道执行求和的属性(作为输入)将具有可以从属性数组开始而不是从实际数组开始的优势(如这个解决方案)。
                【解决方案10】:

                要创建结果/减少值,您应该使用.reduce() 方法而不是.map()

                let data = [
                  {costOfAirtickets: 2500, costOfHotel: 1200},
                  {costOfAirtickets: 1500, costOfHotel: 1000}
                ];
                
                let result = data.reduce(
                  (a, c) => (Object.keys(c).forEach(k => (a[k] = (a[k] || 0) + c[k])), a), {}
                );
                
                console.log(result);

                【讨论】:

                • 简体:const result = data.reduce((a, c) => (Object.keys(c).forEach(k => (a[k] += c[k])), a))
                【解决方案11】:

                map 对象并计算总和并将其存储在另一个中。

                var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
                var result = [];
                var sum = 0;
                var costsum = 0;
                data.map(function(item, key){
                  var cost = item;
                  //nsole.log(cost);
                  sum = sum + cost.costOfAirtickets;
                  costsum = costsum + cost.costOfHotel;
                
                });
                
                result = [{costOfAirtickets:sum, costOfHotel:costsum}];
                
                console.log(result);

                【讨论】:

                  【解决方案12】:

                  您可以通过取一个对象进行求和来减少数组。

                  var data = [{ costOfAirtickets: 2500, costOfHotel: 1200 }, { costOfAirtickets: 1500, costOfHotel: 1000 }],
                      keys = ['costOfAirtickets', 'costOfHotel'],
                      sum = data.reduce((r, o) => {
                          keys.forEach(k => r[k] += o[k]);
                          return r;
                      }, Object.assign(...keys.map(k => ({ [k]: 0 }))));
                  
                  console.log(sum);

                  【讨论】:

                  • 应该是最好的答案。易于定制和动态。
                  • 当使用打字稿关闭错误:Expected at least 1 arguments, but got 0 or more.ts(2557) lib.es2015.core.d.ts(313, 12): An argument for 'target' was not provided. on Object.assign(...keys 你可以做Object.assign({}, ...keys
                  • 对,但如果你没有一些钥匙的话,整体就没有意义了。
                  【解决方案13】:

                  尝试只使用reduce,如下面的sn-p。尝试避免多次迭代。希望下面的 sn-p 有所帮助!

                  var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]
                  
                  var total = data.reduce(function (result,value,key) {
                  result['costOfAirtickets'] = result['costOfAirtickets']  + value['costOfAirtickets'];
                  result['costOfHotel'] = result['costOfHotel']  + value['costOfHotel'];
                  
                  return result},{costOfAirtickets:0,costOfHotel:0});
                  
                  console.log(total)

                  【讨论】:

                    【解决方案14】:

                    您可以为此使用简单的forEach() 循环:

                    var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
                    var res = [];
                    var tempObj = {};
                    data.forEach(({costOfAirtickets, costOfHotel}) => {
                      tempObj['costOfAirtickets'] = (tempObj['costOfAirtickets'] || 0) + costOfAirtickets;
                      tempObj['costOfHotel'] = (tempObj['costOfHotel'] || 0) + costOfHotel;
                     });
                    res.push(tempObj);
                    console.log(res);

                    【讨论】:

                    • 为什么使用点的简单属性的括号?
                    猜你喜欢
                    • 2016-11-21
                    • 2016-09-25
                    • 1970-01-01
                    • 2020-01-07
                    • 1970-01-01
                    • 2018-11-25
                    • 2019-07-03
                    • 1970-01-01
                    • 2023-03-29
                    相关资源
                    最近更新 更多