JavaScript根据属性对对象进行分组

10

我正在尝试通过日期对数组中的对象进行分组:

var list = [
     {
         date: "2017-01-01",
         type: "type1",
         amount: 100
     },
     {
         date: "2017-01-01",
         type: "type2",
         amount: 150
     },
     {
         date: "2017-01-02",
         type: "type1",
         amount: 200
     }
]

我想要得到类似于这样的东西:

var dateArr = [
   {
      date: "2017-01-01",
      activities: [
        {
           type: "type1",
           amount: 100
        },
        {
           type: "type2",
           amount: 150
        }]
   }
]

我已经尝试了一些方法……比如使用underscore(来自这里https://dev59.com/6WUo5IYBdhLWcg3woAqu#15888912):

var dateArr = _
.chain(list)
.groupBy('date')
.map(function(value, key) {
    return {
        date: key,
        activities: [{
            type: _.pluck(value, 'type'),
            amount: _.pluck(value, 'amount')
        }]
    }
})
.value();

我也尝试过这个(来自这里https://stackoverflow.com/a/31373860/4989305

var dateArr = {};
list.forEach(function(item){
    dateArr[item.date] = dateArr[item.date]||[];
    dateArr[item.date].push(item);
});

但是,由于某些原因两者都返回空。

非常感谢任何帮助。


1
list.forEach( 这个可以正常工作。 dateArr 不是一个数组,而是一个对象。在你的最后一段代码后尝试使用 console.log(dateArr) - Cerbrus
你的第一次尝试返回了你描述的完全相同的输出。正如Cerbrus所指出的那样,第二个也可以工作。看起来你观察结果的方式有问题。 - JLRishe
嗯...起码可以说是好奇...我再看一遍。 但是console.log给我返回了空数组。我需要重新逐个检查一遍。不过还是感谢你的帮助——至少我现在知道了它原则上应该是能工作的。 - Funktion
啊 - 搞定了。原来是一个 IndexedDB 事务没有及时完成。Dexie 的 then() 函数解决了这个问题。 - Funktion
3个回答

24
几行现代JavaScript代码就可以得到你想要的结果:
var dateArr = Object.values(list.reduce((result, {
    date,
    type,
    amount
}) => {
    // Create new group
    if (!result[date]) result[date] = {
        date,
        activities: []
    };
    // Append to group
    result[date].activities.push({
        type,
        amount
    });
    return result;
}, {}));

解释:

  1. 使用 Array.reduce 将列表合并为一组结果,即按日期分组的普通对象。
  2. 合并函数 解构 项目为三个参数。
  3. 如果需要,它会创建一个新组。
  4. 当前项目的类型和金额将作为 对象字面量 的一部分推送到该组中。
  5. 相同的集合返回给 reduce,以便下一个项目将合并到同一集合中。
  6. 使用 Object.values 从集合中提取值。(去掉键)

6
你可以使用哈希表对日期进行分组,然后将单个分组分配给结果数组。

var list = [{ date: "2017-01-01", type: "type1", amount: 100 }, { date: "2017-01-01", type: "type2", amount: 150 }, { date: "2017-01-02", type: "type1", amount: 200 }],
    result = [];

list.forEach(function (hash) {
    return function (a) {
        if (!hash[a.date]) {
            hash[a.date] = { date: a.date, activities: []};
            result.push(hash[a.date]);
        }
        hash[a.date].activities.push({ type: a.type, amount: a.amount });
    };
}(Object.create(null)));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


4
我希望您需要的是这个。

var list = [
     {
         date: "2017-01-01",
         type: "type1",
         amount: 100
     },
     {
         date: "2017-01-01",
         type: "type2",
         amount: 150
     },
     {
         date: "2017-01-02",
         type: "type1",
         amount: 200
     }
]
var dateArrKeyHolder = [];
var dateArr = [];
list.forEach(function(item){
    dateArrKeyHolder[item.date] = dateArrKeyHolder[item.date]||{};
    var obj = dateArrKeyHolder[item.date];
    if(Object.keys(obj).length == 0)
    dateArr.push(obj);
    
    obj.date = item.date;
    obj.activities  = obj.activities || [];
    
    obj.activities.push({type:item.type, amount: item.amount });
});


console.log(JSON.stringify(dateArr));


@Funktion 一年过去了,你的比较/测试结果如何? :P - Pascal
@Pascal 很不幸,我目前无法访问该项目,但我记得我使用了 chindirala 的最后一个解决方案——只是因为我尝试过它并且直接奏效。实际上,我可能需要在当前项目中再次使用它,所以我会看看它的表现如何。 - Funktion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接