Удобство Object.GroupBy() в JavaScript
Это был обычный день программирования, когда я решил вернуться к своему старому проекту. Просматривая свой код, я заметил фрагмент кода, написанный мной в первые дни моего путешествия по JavaScript. Проект представлял собой веб-приложение для отслеживания расходов, которое позволяло пользователям вручную вводить транзакции и сохранять их в localStorage. Конкретный фрагмент кода, с которым я столкнулся, был связан с группировкой данных.
Тогда, помню, я боролся с проблемами группировки данных и пытался найти решения с использованием filter()
, map()
и даже forEach()
. Однако, к моему удивлению, я обнаружил, что существует реальный метод, разработанный для удобной группировки данных.
Пример из фактического набора данных, подлежащих обработке, выглядит следующим образом:
const transacHistory = [
{ type: 'Income', desc: 'Salary', amount: '52000', date: '09/01/2023' },
{ type: 'Expense', desc: 'House rent', amount: '7500', date: '09/03/2023'},
{ type: 'Expense', desc: 'Shopping', amount: '8000', date: '09/11/2023' },
{ type: 'Income', desc: 'YouTube', amount: '3508', date: '09/12/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '550', date: '9/02/2023' },
{ type: 'Expense', desc: 'Internet', amount: '2250', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '350', date: '9/18/2023' },
]
Теперь я хотел сгруппировать их в зависимости от типа совершаемой транзакции (расход или доход).
Техника группировки данных старой школы
const groupedTransacHistory = transacHistory.reduce((acc, item)=>{
if(!Object.keys(acc).includes(item.type)){
acc[item.type] = [item]
}
else{
acc[item.type].push(item)
}
return acc;
}, [])
console.log(groupedTransacHistory)
{*
OUTPUT:
[
Income: [
{ type: 'Income', desc: 'Salary', amount: '52000', date: '09/01/2023' },
{ type: 'Income', desc: 'YouTube', amount: '3508', date: '09/12/2023' }
],
Expense: [
{ type: 'Expense', desc: 'House rent', amount: '7500', date: '09/03/2023' },
{ type: 'Expense', desc: 'Shopping', amount: '8000', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '550', date: '9/02/2023' },
{ type: 'Expense', desc: 'Internet', amount: '2250', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '350', date: '9/18/2023' }
]
]
*}
Код для достижения вышеуказанного результата может показаться немного сложным, но любой, кто знаком с filter()
методом, может понять логику. Та же логика также может быть реализована с использованием методов map()
и forEach()
. Поскольку их логика схожа, я включил только код, связанный с filter()
методом.
Узнайте больше об groupBy() методе:
Прежде чем сообщить вам о реальном решении с использованием предложенного groupBy()
метода, давайте в первую очередь проанализируем, как его использовать.
Метод Object.groupBy()
используется для категоризации отдельных элементов итеративного объекта. Категоризация выполняется в соответствии с принудительными строковыми значениями, возвращаемыми предоставленной функцией обратного вызова. Возвращаемый таким образом сгруппированный/категоризированный объект содержит уникальные ключи, каждый из которых соответствует массиву связанных значений. Это звучит непонятно? Вы на правильном пути. Следуйте дальше, чтобы увидеть этот метод в действии.
Синтаксис
Где:
iterable
- это элемент, который нужно сгруппироватьcallbackFunction
- это функция, которая передается для выполнения каждого элемента в итерации. Эта функция должна возвращать значение, которое затем преобразуется в строку.function callbackFunction(element, index){...}
callbackFunction
- принимаетelement
иindex
в качестве своих аргументовelement
представляет текущий элемент, который передается вcallbackFunction
в данный момент, аindex
является индексомelement
Давайте рассмотрим несколько общих и простых примеров, чтобы понять концепцию.
Примеры
Следующий код группирует элементы в зависимости от того, начинаются ли они с гласной или с согласной.
1. Традиционный подход:
const vowels = ['a', 'e', 'i', 'o', 'u']
const cartItems = ['apples', 'bananas', 'cherries', 'umbrellas', 'cat food', 'pedigree', 'cupcakes']
const res2 = cartItems.reduce((accumulator, item) => {
if (vowels.includes(item.slice(0, 1))) {
if (!accumulator['vowels']) {
accumulator['vowels'] = [];
}
accumulator['vowels'].push(item);
} else {
if (!accumulator['non-vowels']) {
accumulator['non-vowels'] = [];
}
accumulator['non-vowels'].push(item);
}
return accumulator;
}, {});
{*
OUTPUT:
{
vowels: ['apples', 'umbrellas'],
non-vowels: ['bananas', 'cherries', 'cat food', 'pedigree', 'cupcakes']
}
*}
2. Object.groupBy()
подход:
const vowels = ['a', 'e', 'i', 'o', 'u']
const cartItems = ['apples', 'bananas', 'cherries', 'umbrellas', 'cat food', 'pedigree', 'cupcakes']
Object.groupBy(cartItems, (item)=>vowels.includes(item.slice(0, 1)))
//item.slice(0,1) returns the first letter of the currently iterated item and checks whether it is a vowel.
{*
OUTPUT:
{
true: ['apples', 'umbrellas'],
false: ['bananas', 'cherries', 'cat food', 'pedigree', 'cupcakes']
}
*}
Если нет свойств объекта, которые должны отображаться в случае массивов, Object.groupBy()
метод просто использует логические значения в качестве ключей. Если нам нужны точно установленные ключи, то мы должны реализовать Map.groupBy()
метод, который является точной копией нашего предыдущего метода, но позволяет нам устанавливать ключи вместо логического значения по умолчанию.
const fictionalCharacters = [
{ character: 'Ironman', ability: 'Fights bad guys' },
{ character: 'Barbie', ability: "Doesn't fight" },
{ character: 'Black widow', ability: 'Fights bad guys' },
{ character: 'Oggy', ability: "Doesn't fight" },
{ character: 'Peppa Pig', ability: "Doesn't fight" }
]
Object.groupBy( fictionalCharacters, (item)=>item.ability )
{* The value of item.ability is type-converted into a string and used as a key to group similar items in arrays, where the key's value is an array of corresponding items. Example: { item.ability: [{}, {}] } *}
{*
OUTPUT:
[
Fights bad guys: [
{ character: 'Ironman', ability: 'Fights bad guys' },
{ character: 'Black widow', ability: 'Fights bad guys' },
],
Doesn't fight: [
{ character: 'Barbie', ability: "Doesn't fight" },
{ character: 'Oggy', ability: "Doesn't fight" },
{ character: 'Peppa Pig', ability: "Doesn't fight" }
]
]
*}
Итак, поскольку теперь у нас есть довольно приличное представление о том, как это работает, давайте вернемся к нашему примеру отслеживания расходов.
const transacHistory = [
{ type: 'Income', desc: 'Salary', amount: '52000', date: '09/01/2023' },
{ type: 'Expense', desc: 'House rent', amount: '7500', date: '09/03/2023'},
{ type: 'Expense', desc: 'Shopping', amount: '8000', date: '09/11/2023' },
{ type: 'Income', desc: 'YouTube', amount: '3508', date: '09/12/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '550', date: '9/02/2023' },
{ type: 'Expense', desc: 'Internet', amount: '2250', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '350', date: '9/18/2023' },
]
Object.groupBy(transacHistory, (item)=>item.type)
{*
OUTPUT:
[
Income: [
{ type: 'Income', desc: 'Salary', amount: '52000', date: '09/01/2023' },
{ type: 'Income', desc: 'YouTube', amount: '3508', date: '09/12/2023' }
],
Expense: [
{ type: 'Expense', desc: 'House rent', amount: '7500', date: '09/03/2023' },
{ type: 'Expense', desc: 'Shopping', amount: '8000', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '550', date: '9/02/2023' },
{ type: 'Expense', desc: 'Internet', amount: '2250', date: '09/11/2023' },
{ type: 'Expense', desc: 'Veggies', amount: '350', date: '9/18/2023' }
]
]
*}
Вот так просто. Вот как метод groupBy()
удобен в использовании и понимании.
ПРИМЕЧАНИЕ:
- Вместо того, чтобы делать огромный скачок к статическим методам, таким как
Object.groupBy()
, новичкам должно быть удобно использовать методыmap()
,filter()
иreduce()
. - Метод
Object.groupBy()
по состоянию на 17 декабря 2023 года поддерживается не во всех браузерах, и ожидается, что это будет в ближайшем будущем.