在javascript中按属性对对象进行分组

IT技术 javascript underscore.js
2021-01-17 15:02:33

如何转换这个:

[
    {food: 'apple', type: 'fruit'},
    {food: 'potato', type: 'vegetable'},
    {food: 'banana', type: 'fruit'},
]

进入这个:

[
    {type: 'fruit', foods: ['apple', 'banana']},
    {type: 'vegetable', foods: ['potato']}
]

使用 javascript 或下划线

6个回答

假设原始列表包含在名为 的变量中list

_
.chain(list)
.groupBy('type')
.map(function(value, key) {
    return {
        type: key,
        foods: _.pluck(value, 'food')
    }
})
.value();
当您有下划线 + mongoDB 时,谁需要冗长的 SQL!?
2021-03-30 15:02:33

不使用下划线:

var origArr = [
    {food: 'apple', type: 'fruit'},
    {food: 'potato', type: 'vegetable'},
    {food: 'banana', type: 'fruit'}
];

/*[
    {type: 'fruit', foods: ['apple', 'banana']},
    {type: 'vegetable', foods: ['potato']}
]*/

function transformArr(orig) {
    var newArr = [],
        types = {},
        i, j, cur;
    for (i = 0, j = orig.length; i < j; i++) {
        cur = orig[i];
        if (!(cur.type in types)) {
            types[cur.type] = {type: cur.type, foods: []};
            newArr.push(types[cur.type]);
        }
        types[cur.type].foods.push(cur.food);
    }
    return newArr;
}

console.log(transformArr(origArr));

演示: http : //jsfiddle.net/ErikE/nSLua/3/

幸得@ErikE为提高/降低我的原代码,以帮助冗余我不得不:)

不需要索引间接。我觉得新代码很尴尬。请看这个
2021-03-15 15:02:33
@ErikE 我认为这比你建议的要多一点,但我已经忘记了它以前的样子。它似乎可以工作,并且不需要 2 个循环,所以看起来不错 :)
2021-03-24 15:02:33
建议:放入newArr.push(types[cur.type]);您的if块内并for完全删除第二个循环。
2021-03-25 15:02:33
@ErikE 是的,我已经在重构了。我知道双循环不好,所以我回去了。等等,也许我正在做的就是你所说的。
2021-03-28 15:02:33
@ErikE 啊,是的,我又被愚弄了。我忘记这样做了,新对象是通过引用推送的(我没有意识到),因此您可以修改原始对象(在types),它将反映在newArr. 感谢您指出了这一点!虽然我几乎不会说我的旧代码很尴尬……至少对我来说它是一种“查找”:)
2021-04-02 15:02:33

这是@Ian 答案的一个稍微不同但更通用的版本

警告:结果与 OP 要求略有不同,但像我这样偶然发现这个问题的其他人可能会受益于更通用的答案恕我直言

var origArr = [
   {food: 'apple', type: 'fruit'},
   {food: 'potato', type: 'vegetable'},
   {food: 'banana', type: 'fruit'}
];

function groupBy(arr, key) {
        var newArr = [],
            types = {},
            newItem, i, j, cur;
        for (i = 0, j = arr.length; i < j; i++) {
            cur = arr[i];
            if (!(cur[key] in types)) {
                types[cur[key]] = { type: cur[key], data: [] };
                newArr.push(types[cur[key]]);
            }
            types[cur[key]].data.push(cur);
        }
        return newArr;
}

console.log(groupBy(origArr, 'type'));

你可以在这里找到一个jsfiddle

你好呀。这正是我要找的!谢谢!-- 我认为展示console.log打印出来的内容是个好主意
2021-03-17 15:02:33
未使用的变量 newItem 和 j。为什么?
2021-03-31 15:02:33

这个老问题的 ES6 解决方案:

迭代 using Array#reduce,并按组将项目收集到Map. 使用 spread 将Map#values返回转换为数组:

const data = [
    {food: 'apple', type: 'fruit'},
    {food: 'potato', type: 'vegetable'},
    {food: 'banana', type: 'fruit'},
];

const result = [...data.reduce((hash, { food, type }) => {
  const current = hash.get(type) || { type, foods: [] };
  
  current.foods.push({ food });
  
  return hash.set(type, current);
}, new Map).values()];

console.log(result);

var foods = [
    {food: 'apple', type: 'fruit'},
    {food: 'potato', type: 'vegetable'},
    {food: 'banana', type: 'fruit'}
];

var newFoods = _.chain( foods ).reduce(function( memo, food ) {
  memo[ food.type ] = memo[ food.type ] || [];
  memo[ food.type ].push( food.food );
  return memo;
}, {}).map(function( foods, type ) {
    return {
        type: type,
        foods: foods
    };
}).value();

http://jsbin.com/etaxh/2/edit