Кто-нибудь знает (если возможно, lodash) способ сгруппировать массив объектов по ключу объекта, а затем создать новый массив объектов на основе группировки? Например, у меня есть массив автомобильных объектов:
var 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'
},
];
Я хочу создать новый массив объектов автомобиля, сгруппированных по make
:
var cars = {
'audi': [
{
'model': 'r8',
'year': '2012'
}, {
'model': 'rs5',
'year': '2013'
},
],
'ford': [
{
'model': 'mustang',
'year': '2012'
}, {
'model': 'fusion',
'year': '2015'
}
],
'kia': [
{
'model': 'optima',
'year': '2012'
}
]
}
groupBy
?your_array.groupBy(...)
Ответы:
Тимо отвечает , как бы я это сделал. Простые
_.groupBy
и допускают некоторые дублирования объектов в сгруппированной структуре.Однако OP также попросил
make
удалить повторяющиеся ключи. Если вы хотите пройти весь путь:var grouped = _.mapValues(_.groupBy(cars, 'make'), clist => clist.map(car => _.omit(car, 'make'))); console.log(grouped);
Урожайность:
{ audi: [ { model: 'r8', year: '2012' }, { model: 'rs5', year: '2013' } ], ford: [ { model: 'mustang', year: '2012' }, { model: 'fusion', year: '2015' } ], kia: [ { model: 'optima', year: '2012' } ] }
Если вы хотите сделать это с помощью Underscore.js, обратите внимание, что его версия
_.mapValues
вызывается_.mapObject
.источник
В простом Javascript вы можете использовать
Array#reduce
с объектомvar 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' }], result = cars.reduce(function (r, a) { r[a.make] = r[a.make] || []; r[a.make].push(a); return r; }, Object.create(null)); console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
источник
result
результаты?Object.entries
и пройти по парам ключ / значение.make
из набора данных после группировки? Это занимает лишнее место.Вы ищите
_.groupBy()
.Удаление свойства, которое вы группируете из объектов, должно быть тривиальным, если необходимо:
var 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'},]; var grouped = _.groupBy(cars, function(car) { return car.make; }); console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>
В качестве бонуса вы получаете еще более приятный синтаксис со стрелочными функциями ES6:
const grouped = _.groupBy(cars, car => car.make);
источник
var grouped = _.groupBy(cars, 'make');
функция вообще не нужна, если аксессор - это простое имя свойства.Нет абсолютно никаких причин загружать стороннюю библиотеку для решения этой простой проблемы, как предполагают приведенные выше решения.
Однострочная версия для группировки
list
объектов по определенномуkey
в es6:const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})
Более длинная версия, которая фильтрует объекты без
key
:function groupByKey(array, key) { return array .reduce((hash, obj) => { if(obj[key] === undefined) return hash; return Object.assign(hash, { [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}) }, {}) } var 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'}]; console.log(groupByKey(cars, 'make'))
ПРИМЕЧАНИЕ. Похоже, что в исходном вопросе задается вопрос, как сгруппировать автомобили по маркам, но не указывать марку в каждой группе. Таким образом, короткий ответ без сторонних библиотек будет выглядеть так:
const groupByKey = (list, key, {omitKey=false}) => list.reduce((hash, {[key]:value, ...rest}) => ({...hash, [value]:( hash[value] || [] ).concat(omitKey ? {...rest} : {[key]:value, ...rest})} ), {}) var 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'}]; console.log(groupByKey(cars, 'make', {omitKey:true}))
источник
Вот ваша собственная
groupBy
функция, которая является обобщением кода из: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscorefunction groupBy(xs, f) { return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {}); } 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 result = groupBy(cars, (c) => c.make); console.log(result);
источник
var 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' }].reduce((r, car) => { const { model, year, make } = car; r[make] = [...r[make] || [], { model, year }]; return r; }, {}); console.log(cars);
источник
Это также возможно с помощью простого
for
цикла:const result = {}; for(const {make, model, year} of cars) { if(!result[make]) result[make] = []; result[make].push({ model, year }); }
источник
for ( let { TABLE_NAME, ...fields } of source) { result[TABLE_NAME] = result[TABLE_NAME] || []; result[TABLE_NAME].push({ ...fields }); }
Я бы оставил
REAL GROUP BY
для примера JS Arrays точно такую же задачу здесьconst inputArray = [ { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" }, { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" } ]; var outObject = inputArray.reduce(function(a, e) { // GROUP BY estimated key (estKey), well, may be a just plain key // a -- Accumulator result object // e -- sequentally checked Element, the Element that is tested just at this itaration // new grouping name may be calculated, but must be based on real value of real field let estKey = (e['Phase']); (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e); return a; }, {}); console.log(outObject);
источник
Вы можете попробовать изменить объект внутри функции, вызываемой для каждой итерации с помощью _.groupBy func. Обратите внимание, что исходный массив меняет свои элементы!
var res = _.groupBy(cars,(car)=>{ const makeValue=car.make; delete car.make; return makeValue; }) console.log(res); console.log(cars);
источник
make
свойства, к тому же оно более читабельно.Создайте метод, который можно использовать повторно
Array.prototype.groupBy = function(prop) { return this.reduce(function(groups, item) { const val = item[prop] groups[val] = groups[val] || [] groups[val].push(item) return groups }, {}) };
Затем ниже вы можете сгруппировать по любым критериям
const groupByMake = cars.groupBy('make'); console.log(groupByMake);
var 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' }, ]; //re-usable method Array.prototype.groupBy = function(prop) { return this.reduce(function(groups, item) { const val = item[prop] groups[val] = groups[val] || [] groups[val].push(item) return groups }, {}) }; // initiate your groupBy. Notice the recordset Cars and the field Make.... const groupByMake = cars.groupBy('make'); console.log(groupByMake); //At this point we have objects. You can use Object.keys to return an array
источник
Для случаев, когда ключ может быть нулевым, и мы хотим сгруппировать их как другие
var 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'}, {'make':'kia','model':'optima','year':'2033'}, {'make':null,'model':'zen','year':'2012'}, {'make':null,'model':'blue','year':'2017'}, ]; result = cars.reduce(function (r, a) { key = a.make || 'others'; r[key] = r[key] || []; r[key].push(a); return r; }, Object.create(null));
источник
Версия прототипа также с использованием ES6. В основном это использует функцию уменьшения для передачи аккумулятора и текущего элемента, который затем использует это для построения ваших «сгруппированных» массивов на основе переданного ключа. внутренняя часть сокращения может выглядеть сложным, но по сути это проверка, чтобы увидеть, существует ли ключ переданного в объекте, и если он не создает пустой массив и добавляет текущий элемент к этому вновь созданному массиву, в противном случае, используя распространение оператор передает все объекты текущего массива ключей и добавляет текущий элемент. Надеюсь, это кому-то поможет !.
Array.prototype.groupBy = function(k) { return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{}); }; const projs = [ { project: "A", timeTake: 2, desc: "this is a description" }, { project: "B", timeTake: 4, desc: "this is a description" }, { project: "A", timeTake: 12, desc: "this is a description" }, { project: "B", timeTake: 45, desc: "this is a description" } ]; console.log(projs.groupBy("project"));
источник
Вы также можете использовать
array#forEach()
такой метод: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' }]; let newcars = {} cars.forEach(car => { newcars[car.make] ? // check if that array exists or not in newcars object newcars[car.make].push({model: car.model, year: car.year}) // just push : (newcars[car.make] = [], newcars[car.make].push({model: car.model, year: car.year})) // create a new array and push }) console.log(newcars);
источник
function groupBy(data, property) { return data.reduce((acc, obj) => { const key = obj[property]; if (!acc[key]) { acc[key] = []; } acc[key].push(obj); return acc; }, {}); } groupBy(people, 'age');
источник
Я люблю писать его без зависимости / сложности, просто чистый простой js.
const mp = {} const cars = [ { model: 'Imaginary space craft SpaceX model', year: '2025' }, { 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' } ] cars.forEach(c => { if (!c.make) return // exit (maybe add them to a "no_make" category) if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }] else mp[c.make].push({ model: c.model, year: c.year }) }) console.log(mp)
источник
Я сделал тест, чтобы проверить производительность каждого решения, не использующего внешние библиотеки.
JSBen.ch
reduce()
Вариант, отправленный @Nina Scholz кажется оптимальным.источник
Мне понравился ответ @metakunfu, но он не дает точно ожидаемого результата. Вот обновление, которое избавляет от «make» в окончательной полезной нагрузке JSON.
var 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' }, ]; result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {}) console.log(JSON.stringify(result));
Выход:
{ "audi":[ { "model":"r8", "year":"2012" }, { "model":"rs5", "year":"2013" } ], "ford":[ { "model":"mustang", "year":"2012" }, { "model":"fusion", "year":"2015" } ], "kia":[ { "model":"optima", "year":"2012" } ] }
источник
Просто попробуйте этот, он отлично подходит для меня.
let grouped = _.groupBy(cars, 'make');
источник
С помощью lodash / fp вы можете создать функцию с
_.flow()
этой 1-й группой по ключу, а затем сопоставить каждую группу и опустить ключ из каждого элемента:const { flow, groupBy, mapValues, map, omit } = _; const groupAndOmitBy = key => flow( groupBy(key), mapValues(map(omit(key))) ); 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 groupAndOmitMake = groupAndOmitBy('make'); const result = groupAndOmitMake(cars); console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
источник
Основываясь на ответе @Jonas_Wilms, если вы не хотите вводить все свои поля:
var result = {}; for ( let { first_field, ...fields } of your_data ) { result[first_field] = result[first_field] || []; result[first_field].push({ ...fields }); }
Я не проводил никаких тестов, но считаю, что использование цикла for будет более эффективным, чем все, что предлагается в этом ответе .
источник
const reGroup = (list, key) => { const newGroup = {}; list.forEach(item => { const newItem = Object.assign({}, item); delete newItem[key]; newGroup[item[key]] = newGroup[item[key]] || []; newGroup[item[key]].push(newItem); }); return newGroup; };
const animals = [ { type: 'dog', breed: 'puddle' }, { type: 'dog', breed: 'labradoodle' }, { type: 'cat', breed: 'siamese' }, { type: 'dog', breed: 'french bulldog' }, { type: 'cat', breed: 'mud' } ]; console.log(reGroup(animals, 'type'));
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' }, ]; console.log(reGroup(cars, 'make'));
источник
Сгруппированный массив объектов в машинописном тексте следующим образом:
groupBy (list: any[], key: string): Map<string, Array<any>> { let map = new Map(); list.map(val=> { if(!map.has(val[key])){ map.set(val[key],list.filter(data => data[key] == val[key])); } }); return map; });
источник
your_array.groupBy(...)
Вот еще одно решение. Как просили.
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())
источник
Вот решение, вдохновленное Collectors.groupingBy () в Java:
function groupingBy(list, keyMapper) { return list.reduce((accummalatorMap, currentValue) => { const key = keyMapper(currentValue); if(!accummalatorMap.has(key)) { accummalatorMap.set(key, [currentValue]); } else { accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue)); } return accummalatorMap; }, new Map()); }
Это даст объект Map.
// Usage const carMakers = groupingBy(cars, car => car.make);
источник