【问题标题】:Combining objects in an array with same key (date)将具有相同键(日期)的数组中的对象组合起来
【发布时间】:2018-01-26 13:09:40
【问题描述】:

我有一个对象数组,我想组合具有相同日期值的对象,所以我有一个类似的数组。我正在使用 .map(i => ) 遍历它们并检查前一个索引,但我得到了错误的值。

 [{ id: '8',
   name: 'hfh',
   consumed_date: '2017-03-14',
   calories: 8952 },
 { id: '7',
   name: 'onion',
   consumed_date: '2017-03-14',
   calories: 224},
 { id: '6',
   name: 'onion',
   consumed_date: '2017-03-14',
   calories: 279},
 { id: '2',
   name: 'Ready-to-Serve Chocolate Drink',
   consumed_date: '2017-01-01',
   calories: 3036} ]

我想得到一个像

这样的数组
[{date: 2017-03-14,
calories: 9455},
date: 2017-01-01,
calories: 3036}]

【问题讨论】:

  • 你应该看看reduce。
  • 就像在您的示例中一样,您可能将数据组合成一个长度 > 1 的数组。因此,array.reduce 不起作用,因为我认为 reduce 总是会返回一个结果。跨度>
  • 但是你可以用 [] 来初始化它并在reduction里面修改这个数组。

标签: javascript arrays sorting for-loop ecmascript-6


【解决方案1】:

大概是这样的:

Object.values(arr.reduce(function(obj, entry){
    if(!(entry.consumed_date in obj)){
        obj[entry.consumed_date] = {
            date: entry.consumed_date,
            calories: 0
        };
    }
    obj[entry.consumed_date].calories += entry.calories;

    return obj;
}, {}));

https://jsfiddle.net/DerekL/9gyL9dfs/

【讨论】:

    【解决方案2】:

    这大量使用了 ES 6 的特性,如箭头函数、参数解构、Object.entries、对象速记属性等。确保转译。

    Object.entries(arr.reduce((acc, record) => {
      if (!acc[record.consumed_date]) acc[record.consumed_date] = 0;
      acc[record.consumed_date] += record.calories;
      return acc;
    }, {})).map(([date, calories]) => {date, calories});
    

    【讨论】:

      【解决方案3】:
      let array =  [{ id: '8',
          name: 'hfh',
          consumed_date: '2017-03-14',
          calories: 8952 },
          { id: '7',
              name: 'onion',
              consumed_date: '2017-03-14',
              calories: 224},
          { id: '6',
              name: 'onion',
              consumed_date: '2017-03-14',
              calories: 279},
          { id: '2',
              name: 'Ready-to-Serve Chocolate Drink',
              consumed_date: '2017-01-01',
              calories: 3036} ]
      
      let newArray = array.reduce((sum, value)=>{
          if(value.consumed_date === '2017-03-14') {
              return sum.concat(value)
          }else{
              return sum
          }
      }, []);
      
      console.log(newArray);
      

      【讨论】:

        【解决方案4】:
        var after = {},
            found;
        
        for (var i = 0; i < arr.length; i++) {
          found = false;
          for (var j = 0; j < after.length; j++) {
            if (arr[i]['consumed_date'] == after[j]['date']) {
              after[j]['calories'] += arr[i]['calories'];
              found = true;
              break;
            }
          }
          if (!found) {
            after[after.length] = {
              'date': arr[i]['consumed_date'],
              'calories': arr[i]['calories']
            }
          }
        }
        

        将较大的数组(包含每日条目)命名为arr,或在整个代码中更改arr。然后这些值将根据要求分配给after

        注意 这里的例子很差,也没有经过测试,但我已经尽力了:/

        【讨论】:

        • 如果我在这里犯了一个愚蠢的错误,请指出并原谅我哈哈。我刚刚重新开始 Web 开发,所以我有点生疏了。编辑 - 刚刚通读,现在将修复它,因为它显然不会工作
        【解决方案5】:

        首先,将通过使用消费日期作为索引来减少数据,然后整个数据将通过 Object.entries 转换为数组并映射该结果。

        var json = [{ id: '8',
           name: 'hfh',
           consumed_date: '2017-03-14',
           calories: 8952 },
         { id: '7',
           name: 'onion',
           consumed_date: '2017-03-14',
           calories: 224},
         { id: '6',
           name: 'onion',
           consumed_date: '2017-03-14',
           calories: 279},
         { id: '2',
           name: 'Ready-to-Serve Chocolate Drink',
           consumed_date: '2017-01-01',
           calories: 3036} ];
           
          var newar = json.reduce((acc, val) => {
            if(acc[val.consumed_date] === undefined) {
              acc[val.consumed_date] = { date: val.consumed_date, calories: val.calories };
            }
            else {
              acc[val.consumed_date].calories = acc[val.consumed_date].calories + val.calories;
            }
        
            return acc;
           }, []);
           
           document.getElementById("content").innerHTML = JSON.stringify(Object.entries(newar).map((e) => e[1]));
        &lt;pre id="content"&gt;&lt;/pre&gt;

        【讨论】:

          【解决方案6】:

          对于数组中的每个项目,检查我们是否已经看到它。如果是这样,请添加卡路里。如果不向组合列表中添加新项目。

          如果您可以使用 Lodash:

          var _ = require('lodash')
          
          // Initial Data
          var myArray =  [{ 
            id: '8',
            name: 'hfh',
            consumed_date: '2017-03-14',
            calories: 8952 
          },
          { 
            id: '7',
            name: 'onion',
            consumed_date: '2017-03-14',
            calories: 224
          },
          { 
            id: '6',
            name: 'onion',
            consumed_date: '2017-03-14',
            calories: 279
          },
          { 
            id: '2',
            name: 'Ready-to-Serve Chocolate Drink',
            consumed_date: '2017-01-01',
            calories: 3036
          }]
          
          // sum the calories over the dates
          var result = _(myArray)
            .groupBy('consumed_date')
            .map( (item, key) => ({
              date: key,
              calories: _.sumBy(item, 'calories')
            }))
            .value()
          
          console.log('result', result)
          

          或者你可以自己做这样的事情:

          // Initial Data
          var myArray =  [{ 
            id: '8',
            name: 'hfh',
            consumed_date: '2017-03-14',
            calories: 8952 
          },
          { 
            id: '7',
            name: 'onion',
            consumed_date: '2017-03-14',
            calories: 224
          },
          { 
            id: '6',
            name: 'onion',
            consumed_date: '2017-03-14',
            calories: 279
          },
          { 
            id: '2',
            name: 'Ready-to-Serve Chocolate Drink',
            consumed_date: '2017-01-01',
            calories: 3036
          }]
          
          // helper find function
          function getByDate(list, date) {
            function dateMatches (item) {
              return item.date === date
            }
            return list.find(dateMatches)
          }
          
          // get combined result
          var combinedArray = []
          
          myArray.forEach(function (item) {
            var previousMatch = getByDate(combinedArray, item.consumed_date)
            if (previousMatch) {
              previousMatch.calories += item.calories
            }
            else
            {
              combinedArray.push({
                date: item.consumed_date,
                calories: item.calories
              })
            }
          })
          
          console.log(combinedArray)

          【讨论】:

            【解决方案7】:

            Reduce 非常适合这样的情况:我们需要“循环”所有项目,但我们不希望 map 的直接“a => modify(a)”行为,但是而是需要将几个值减少或折叠成其他值(在这种情况下,输入和输出都是一个数组,但其中一些值被减少为一个)。

            当我们遍历所有项目时,我们需要一些变量来保存状态(到目前为止的进度)。在更命令式的风格中,可以通过在 for 循环之外声明的变量来解决,但在 reduce 中,“到目前为止的进展”-变量 (acc) 与下一项一起为每次迭代传递.

            const reducedArray = arr.reduce((acc, next) => { // acc stands for accumulator
              const lastItemIndex = acc.length -1;
              const accHasContent = acc.length >= 1;
            
              if(accHasContent && acc[lastItemIndex].consumed_date == next.consumed_date) {
                acc[lastItemIndex].calories += next.calories;
              } else {
                // first time seeing this entry. add it!
                acc[lastItemIndex +1] = next;
              }
              return acc;
            }, []);
            
            console.log(reducedArray)
            

            (此解决方案假定数据按日期排序。)

            【讨论】:

            • 我怎样才能做到这一点,以便它推入一个只有卡路里和日期值的新数组?
            • @elcapitan 然后,不要在acc[lastItemIndex +1] = next; 这一行中添加整个项目,而是创建一个仅包含您感兴趣的属性的新对象。像这样:´´acc[lastItemIndex +1] = {消费日期:next.consumed_date,卡路里:next.calories};''。你可以在这里运行整个代码:jsfiddle.net/jonahe/zns9y3ws
            • @elcapitan 对不起,我搞砸了格式。应该是:acc[lastItemIndex +1] = { consumed_date: next.consumed_date, calories: next.calories };。如果您有任何问题,请务必提出更多问题。它现在按预期工作了吗?
            • 哇。不敢相信我没有看到。这是一个很棒的解决方案!非常感谢!
            • @elcaptain 没问题! :) 如果您认为正确,请考虑将答案标记为正确。
            猜你喜欢
            • 1970-01-01
            • 2017-10-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-13
            • 2020-12-16
            相关资源
            最近更新 更多