Самый эффективный способ группировки массивов объектов
Вопрос: Как эффективно сгруппировать объекты в массиве?
У меня есть массив объектов:
[
{ 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" }
]
Я отображаю эту информацию в таблице и хотел бы сгруппировать данные несколькими способами, суммируя значения.
Я использую Underscore.js для функции groupBy
, которая полезна, но не решает поставленную задачу до конца. Мне нужно, чтобы значения не "разделялись", а "сливались", как в методе group by
SQL.
Я ищу способ суммировать специфические значения (если это необходимо). Например, если я сгруппирую по Phase
, я бы хотел получить:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
А если я сделаю группировку по Phase
и Step
, то должен получить:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
Существует ли удобный скрипт для этого, или мне стоит продолжать использовать Underscore.js и затем самостоятельно проходить по результирующему объекту, чтобы суммировать значения?
5 ответ(ов)
Если вы хотите избежать использования внешних библиотек, вы можете лаконично реализовать "группировку по" (groupBy()
) следующим образом:
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
console.log(groupBy(['one', 'two', 'three'], 'length'));
// => {"3": ["one", "two"], "5": ["three"]}
Этот код использует метод reduce()
для обработки массива и группировки элементов по значению указанного ключа. Например, данный пример группирует строки по их длине, в результате чего вы получите объект, где ключами будут длины строк, а значениями — массивы строк соответствующей длины.
Вопрос: Как сгруппировать массив объектов с использованием объекта Map в ES6?
Ответ:
Вы можете использовать объект Map
в ES6 для группировки массивов по определенному критерию. В приведенном ниже коде представлена функция groupBy
, которая принимает массив и функцию для получения ключей, и возвращает Map
, где ключами являются значения, возвращенные функцией, а значениями — массивы объектов, сгруппированные по этим ключам.
Вот реализация функции:
/**
* @description
* Принимает массив и функцию группировки,
* и возвращает Map, сгруппированный по функции.
*
* @param list Массив типа V.
* @param keyGetter Функция, которая принимает элемент массива типа V и возвращает значение типа K.
* K обычно представляет собой ключ свойства V.
*
* @returns Map, сгруппированный по функции.
*/
function groupBy(list, keyGetter) {
const map = new Map();
list.forEach((item) => {
const key = keyGetter(item);
const collection = map.get(key);
if (!collection) {
map.set(key, [item]);
} else {
collection.push(item);
}
});
return map;
}
Пример использования функции:
const pets = [
{type:"Dog", name:"Spot"},
{type:"Cat", name:"Tiger"},
{type:"Dog", name:"Rover"},
{type:"Cat", name:"Leo"}
];
const grouped = groupBy(pets, pet => pet.type);
console.log(grouped.get("Dog")); // -> [{type:"Dog", name:"Spot"}, {type:"Dog", name:"Rover"}]
console.log(grouped.get("Cat")); // -> [{type:"Cat", name:"Tiger"}, {type:"Cat", name:"Leo"}]
const odd = Symbol();
const even = Symbol();
const numbers = [1, 2, 3, 4, 5, 6, 7];
const oddEven = groupBy(numbers, x => (x % 2 === 1 ? odd : even));
console.log(oddEven.get(odd)); // -> [1, 3, 5, 7]
console.log(oddEven.get(even)); // -> [2, 4, 6]
В этом примере функция groupBy
создает Map
для группировки объектов pets
по их типу. Также показано, как использовать символы для группировки чисел по четности.
Дополнительную информацию о Map
можно найти в документации MDN.
Вот перевод вашей функции groupBy
на русский в стиле ответа на StackOverflow:
Для группировки массива объектов по заданному ключу в JavaScript с использованием ES6, вы можете использовать функцию reduce
. Вот пример реализации:
const groupBy = (items, key) => items.reduce(
(result, item) => ({
...result,
[item[key]]: [
...(result[item[key]] || []),
item,
],
}),
{},
);
Как это работает:
Параметры: Функция принимает два аргумента -
items
(массив объектов) иkey
(ключ, по которому нужно группировать).reduce: Мы используем метод
reduce
, который перебирает все элементы массива. В качестве аргументов он принимает:result
— промежуточный объект, в который будут собираться сгруппированные элементы.item
— текущий элемент массива.
Агрегация: Для каждого
item
мы обновляемresult
:- Используем оператор распространения
...
для копирования существующих свойствresult
. - Добавляем новое свойство, ключом которого является значение
item[key]
. - Для значения этого ключа создаём новый массив, который включает в себя существующие элементы (если они есть), а также текущий элемент
item
.
- Используем оператор распространения
Итог: В результате мы получаем объект, в котором ключи - это уникальные значения по указанному ключу, а значения - массивы элементов, сгруппированных по этим ключам.
Пример использования:
const data = [
{ id: 1, category: 'fruit', name: 'apple' },
{ id: 2, category: 'fruit', name: 'banana' },
{ id: 3, category: 'vegetable', name: 'carrot' },
];
const grouped = groupBy(data, 'category');
console.log(grouped);
/*
{
fruit: [
{ id: 1, category: 'fruit', name: 'apple' },
{ id: 2, category: 'fruit', name: 'banana' }
],
vegetable: [
{ id: 3, category: 'vegetable', name: 'carrot' }
]
}
*/
Надеюсь, это поможет! Если возникнут дополнительные вопросы, не стесняйтесь спрашивать.
ES2024 нативные методы Object.groupBy и Map.groupBy
С помощью методов Object.groupBy
и Map.groupBy
, добавленных в ES2024, вы можете удобно группировать элементы массива по определенному критерию. Вот пример использования:
Object.groupBy([1, 2, 3, 4, 5, 6, 7, 8, 9], v => (v % 2 ? "odd" : "even"));
// { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8] };
Map.groupBy([1, 2, 3, 4, 5, 6, 7, 8, 9], v => (v % 2 ? "odd" : "even")).get('odd');
// [1, 3, 5, 7, 9]
Решение для группировки в одну строку с использованием ES2021
Если вам нужно решение, которое будет работать в более ранних версиях JavaScript, вот функция groupBy
, реализованная с использованием метода reduce
:
const groupBy = (x, f) => x.reduce((a, b, i) => ((a[f(b, i, x)] ||=[]).push(b), a), {});
TypeScript
Для TypeScript также можно использовать следующую реализацию:
const groupBy = <T>(array: T[], predicate: (value: T, index: number, array: T[]) => string) =>
array.reduce((acc, value, index) => {
(acc[predicate(value, index, array)] ||= []).push(value);
return acc;
}, {} as { [key: string]: T[] });
ПРИМЕРЫ
Вот несколько примеров использования функции groupBy
:
groupBy([1, 2, 3, 4, 5, 6, 7, 8, 9], v => (v % 2 ? "odd" : "even"));
// { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8] };
const colors = [
"Apricot",
"Brown",
"Burgundy",
"Cerulean",
"Peach",
"Pear",
"Red",
];
groupBy(colors, v => v[0]); // группировка по первой букве цветовых названий
// {
// A: ["Apricot"],
// B: ["Brown", "Burgundy"],
// C: ["Cerulean"],
// P: ["Peach", "Pear"],
// R: ["Red"],
// }
groupBy(colors, v => v.length); // группировка по длине названий цветов
// {
// 3: ["Red"],
// 4: ["Pear"],
// 5: ["Brown", "Peach"],
// 7: ["Apricot"],
// 8: ["Burgundy", "Cerulean"],
// }
const data = [
{ comment: "abc", forItem: 1, inModule: 1 },
{ comment: "pqr", forItem: 1, inModule: 1 },
{ comment: "klm", forItem: 1, inModule: 2 },
{ comment: "xyz", forItem: 1, inModule: 2 },
];
groupBy(data, v => v.inModule); // группировка по модулю
// {
// 1: [
// { comment: "abc", forItem: 1, inModule: 1 },
// { comment: "pqr", forItem: 1, inModule: 1 },
// ],
// 2: [
// { comment: "klm", forItem: 1, inModule: 2 },
// { comment: "xyz", forItem: 1, inModule: 2 },
// ],
// }
groupByToMap
Если вы хотите получить результат в виде Map
, вы можете использовать функцию groupByToMap
:
const groupByToMap = (x, f) =>
x.reduce((a, b, i) => {
const k = f(b, i);
a.get(k)?.push(b) ?? a.set(k, [b]);
return a;
}, new Map());
TypeScript
А вот аналогичная реализация на TypeScript:
const groupByToMap = <T, Q>(array: T[], predicate: (value: T, index: number, array: T[]) => Q) =>
array.reduce((map, value, index) => {
const key = predicate(value, index);
map.get(key)?.push(value) ?? map.set(key, [value]);
return map;
}, new Map<Q, T[]>());
Эти подходы позволяют вам удобно группировать данные в зависимости от ваших потребностей, используя как нативные методы ES2024, так и совместимые с более ранними версиями языка решения.
Конечно! Вот перевод вашего кода на русский в стиле ответа на StackOverflow:
Хотя это немного поздно, возможно, кто-то найдет это полезным.
В ES6 вы можете использовать функцию groupBy
, чтобы сгруппировать массив объектов по определенному ключу. Вот пример, где мы группируем пользователей по их цвету:
const users = [{
name: "Jim",
color: "blue"
},
{
name: "Sam",
color: "blue"
},
{
name: "Eddie",
color: "green"
},
{
name: "Robert",
color: "green"
},
];
const groupBy = (arr, key) => {
const initialValue = {};
return arr.reduce((acc, cval) => {
const myAttribute = cval[key];
acc[myAttribute] = [...(acc[myAttribute] || []), cval];
return acc;
}, initialValue);
};
const res = groupBy(users, "color");
console.log("группировка по цвету:", res);
В этом примере мы используем метод reduce
, чтобы создать объект, в котором ключами будут значения цвета, а значениями — массивы пользователей с соответствующими цветами. В результате выполнения кода в консоль будет выведен объект, сгруппированный по цветам.
Если у вас есть дополнительные вопросы или вы хотите узнать о других способах группировки, не стесняйтесь спрашивать!
Проверка наличия ключа в объекте JavaScript?
Как получить доступ к вложенным объектам, массивам или JSON и обработать их?
Как удалить все дубликаты из массива объектов?
Как перемешать (сделать случайным) массив в JavaScript?
Как объединить два массива в JavaScript и удалить дубликаты?