如何按键将对象数组分组?

402

有没有人知道一种方法(最好用lodash),可以根据对象的一个键将对象数组分组,然后基于这个分组创建一个新的对象数组?例如,我有一个汽车对象数组:

const cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

我想创建一个按 制造商 分组的新汽车对象数组:

const cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}

4
你的结果无效。 - Nina Scholz
有没有类似的方法可以获取一个 Map 而不是一个对象? - Andrea Bergonzo
3
如果您正在使用Typescript(这不是OP的情况),那么您已经具有groupBy方法。 您可以通过 your_array.groupBy(...) 进行使用。 - Isac Moura
9
你的数组没有 "groupBy(...)" 这个方法! - Martijn Hiemstra
这个人创建了一个版本的beta js函数array.group(),它的效果非常棒: https://dmitripavlutin.com/javascript-array-group/ - Facundo Colombier
@NinaScholz 对不起,但他们的期望结果有什么问题吗? - wynx
34个回答

-1
const groupBy = (array, callback) => {
  const groups = {};
  
  array.forEach((element) => {
    const groupName = callback(element);
    if (groupName in groups) {
      groups[groupName].push(element);
    } else {
      groups[groupName] = [element];
    }
  });
  
  return groups;
};

或者对于花俏的裤子:

(() => {
  Array.prototype.groupBy = function (callback) {
    const groups = {};
    this.forEach((element, ...args) => {
      const groupName = callback(element, ...args);
      if (groupName in groups) {
        groups[groupName].push(element);
      } else {
        groups[groupName] = [element];
      }
    });

    return groups;
  };
})();

const res = [{ name: 1 }, { name: 1 }, { name: 0 }].groupBy(({ name }) => name);

// const res = {
//   0: [{name: 0}],
//   1: [{name: 1}, {name: 1}]
// }

这是一个MDN Array.groupBy function的polyfill。


-1

尝试

groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )

const cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

const groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )

console.log('gr', groupBy(cars, o=>o.make));

这篇答案受到了cdiggins的回答和Endless的评论(最终对象中不移除键)的启发。改进之处在于我们有了相同的小尺寸,但是函数接口groupBy(a, f)不包含额外的冗余变量。要获取分组数组的数组,您可以使用Object.values(groupBy(cars, o=>o.make))


-2

这是 @metakungfus answer 的稍微不同的版本,主要区别在于它从结果对象中省略了原始键,因为在某些情况下,在父对象中已经可用,因此不再需要在对象本身上使用。

const groupBy = (_k, a) => a.reduce((r, {[_k]:k, ...p}) => ({
    ...r, ...{[k]: (
        r[k] ? [...r[k], {...p}] : [{...p}]
    )}
}), {});

考虑您的原始输入对象:

console.log(groupBy('make', cars));

会导致:

{
  audi: [
    { model: 'r8', year: '2012' },
    { model: 'rs5', year: '2013' }
  ],
  ford: [
    { model: 'mustang', year: '2012' },
    { model: 'fusion', year: '2015' }
  ],
  kia: [
    { model: 'optima', year: '2012' }
  ]
}

2
非常难读的代码 - Martin

-2

这里有另一种解决方案。如所请求。

我想创建一个按制造商分组的新汽车对象数组:

function groupBy() {
  const key = 'make';
  return cars.reduce((acc, x) => ({
    ...acc,
    [x[key]]: (!acc[x[key]]) ? [{
      model: x.model,
      year: x.year
    }] : [...acc[x[key]], {
      model: x.model,
      year: x.year
    }]
  }), {})
}

输出:

console.log('Grouped by make key:',groupBy())

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