Клавиша / esc

.groupBy()

Группирует элементы итерируемого объекта.

Время чтения: меньше 5 мин

Кратко

Скопировано

Статический метод groupBy() группирует элементы итерируемого объекта, опираясь на переданную колбэк-функцию. Она должна возвращать ключ группы, в которую стоит положить текущий элемент. Object.groupBy() сгруппирует элементы в новый объект, а Map.groupBy() — в новую Map.

Пример Object.groupBy()

Скопировано
        
          
          const colors = [  { value: '50% 0.2 12', name: 'oklch' },  { value: '198, 35, 109', name: 'rgb' },  { value: '55% 0.2 0', name: 'oklch' }]const groupedColors = Object.groupBy(colors, (color, index) =>  color.name === 'oklch' ? 'easyToUnderstand' : 'hardToUnderstand')console.log(groupedColors)/*{  easyToUnderstand: [    { value: '50% 0.2 12', name: 'oklch' },    { value: '55% 0.2 0', name: 'oklch' }  ],  hardToUnderstand: [    { value: '198, 35, 109', name: 'rgb' }  ]}*/
          const colors = [
  { value: '50% 0.2 12', name: 'oklch' },
  { value: '198, 35, 109', name: 'rgb' },
  { value: '55% 0.2 0', name: 'oklch' }
]

const groupedColors = Object.groupBy(colors, (color, index) =>
  color.name === 'oklch' ? 'easyToUnderstand' : 'hardToUnderstand'
)

console.log(groupedColors)
/*
{
  easyToUnderstand: [
    { value: '50% 0.2 12', name: 'oklch' },
    { value: '55% 0.2 0', name: 'oklch' }
  ],
  hardToUnderstand: [
    { value: '198, 35, 109', name: 'rgb' }
  ]
}
*/

        
        
          
        
      

Пример Map.groupBy()

Скопировано

Метод Map.groupBy() будет полезен, если нам в качестве ключей нужно использовать типы, которые не могут быть ключами объекта. Например, другие объекты:

        
          
          const users = {  1: { id: 1, firstName: 'Василий', lastName: 'Пупкин', email: 'vasya@pupkin.person' },  2: { id: 2, firstName: 'Анна', lastName: 'Иванова', email: 'anna@ivanova.person' },  3: { id: 3, firstName: 'Игорь', lastName: 'Сидоров', email: 'igor@sidorov.person' }}const orders = [  { id: 1, customerID: 1, price: 1500, goods: ['Молоко', 'Хлеб'] },  { id: 2, customerID: 2, price: 2500, goods: ['Хлеб', 'Яйца', 'Сыр'] },  { id: 3, customerID: 1, price: 3000, goods: ['Сыр', 'Картофель'] },  { id: 4, customerID: 3, price: 1800, goods: ['Молоко', 'Картофель'] },  { id: 5, customerID: 2, price: 1200, goods: ['Хлеб', 'Сыр'] }]const userOrders = Map.groupBy(orders, order => users[order.customerID])for (const [user, orders] of userOrders) {  console.groupCollapsed(`${user.firstName} ${user.lastName}`)  for (const order of orders)    console.log(`Заказ #${order.id}: ${order.goods.join(', ')} на сумму ${order.price} руб.`)  console.groupEnd()}/*Василий Пупкин  Заказ #1: Молоко, Хлеб на сумму 1500 руб.  Заказ #3: Сыр, Картофель на сумму 3000 руб.Анна Иванова  Заказ #2: Хлеб, Яйца, Сыр на сумму 2500 руб.  Заказ #5: Хлеб, Сыр на сумму 1200 руб.Игорь Сидоров  Заказ #4: Молоко, Картофель на сумму 1800 руб.*/
          const users = {
  1: { id: 1, firstName: 'Василий', lastName: 'Пупкин', email: 'vasya@pupkin.person' },
  2: { id: 2, firstName: 'Анна', lastName: 'Иванова', email: 'anna@ivanova.person' },
  3: { id: 3, firstName: 'Игорь', lastName: 'Сидоров', email: 'igor@sidorov.person' }
}

const orders = [
  { id: 1, customerID: 1, price: 1500, goods: ['Молоко', 'Хлеб'] },
  { id: 2, customerID: 2, price: 2500, goods: ['Хлеб', 'Яйца', 'Сыр'] },
  { id: 3, customerID: 1, price: 3000, goods: ['Сыр', 'Картофель'] },
  { id: 4, customerID: 3, price: 1800, goods: ['Молоко', 'Картофель'] },
  { id: 5, customerID: 2, price: 1200, goods: ['Хлеб', 'Сыр'] }
]

const userOrders = Map.groupBy(orders, order => users[order.customerID])

for (const [user, orders] of userOrders) {
  console.groupCollapsed(`${user.firstName} ${user.lastName}`)

  for (const order of orders)
    console.log(`Заказ #${order.id}: ${order.goods.join(', ')} на сумму ${order.price} руб.`)

  console.groupEnd()
}

/*
Василий Пупкин
  Заказ #1: Молоко, Хлеб на сумму 1500 руб.
  Заказ #3: Сыр, Картофель на сумму 3000 руб.
Анна Иванова
  Заказ #2: Хлеб, Яйца, Сыр на сумму 2500 руб.
  Заказ #5: Хлеб, Сыр на сумму 1200 руб.
Игорь Сидоров
  Заказ #4: Молоко, Картофель на сумму 1800 руб.
*/

        
        
          
        
      

Как пишется

Скопировано

Object.groupBy(items, callbackFn) или Map.groupBy(items, callbackFn), где:

  • items — итерируемый объект, например, массив;
  • callbackFn — колбэк-функция. В неё будут передаваться два аргумента:
    • element — текущий элемент items;
    • index — индекс текущего элемента.

callbackFn должна вернуть ключ, по которому элемент будет добавлен в группу. В случае Object.groupBy() ключом будет строка или символ, в случае Map.groupBy() — любой тип данных.

Поддержка в браузерах:
  • Chrome 117, поддерживается
  • Edge 117, поддерживается
  • Firefox 119, поддерживается
  • Safari 17.4, поддерживается
О Baseline

На практике

Скопировано

Артур Бэйлис Ли советует

Скопировано

Object.groupBy() можно использовать как замену аналогичному методу из популярной, но уже устаревшей библиотеки Lodash.

        
          
          - _.groupBy([6.1, 4.2, 6.3], Math.floor)+ Object.groupBy([6.1, 4.2, 6.3], Math.floor)- _.groupBy(['one', 'two', 'three'], 'length');+ Object.groupBy(['one', 'two', 'three'], word => word.length)
          - _.groupBy([6.1, 4.2, 6.3], Math.floor)
+ Object.groupBy([6.1, 4.2, 6.3], Math.floor)

- _.groupBy(['one', 'two', 'three'], 'length');
+ Object.groupBy(['one', 'two', 'three'], word => word.length)

        
        
          
        
      

Как видно, функции не полностью идентичны. Однако, если в вашей кодовой базе активно используется метод из Lodash, стоит задуматься о замене. Особенно это важно для фронтенда, так как отказ от Lodash уменьшит размер бандла.

Метод groupBy() поддерживается всеми основными браузерами, начиная с марта 2024 года. Если требуется поддержка более старых браузеров, это не проблема — написать полифил довольно легко:

        
          
          if (!('groupBy' in Object)) {  Object.groupBy = (items, getKey) =>    Array.from(items).reduce((result, item, index) => {      const key = getKey(item, index)      result[key] ??= []      result[key].push(item)      return result    }, {})}if (!('groupBy' in Map)) {  Map.groupBy = (items, getKey) =>    Array.from(items).reduce((result, item, index) => {      const key = getKey(item, index)      if (!result.has(key)) result.set(key, [])      result.get(key).push(item)      return result    }, new Map())}
          if (!('groupBy' in Object)) {
  Object.groupBy = (items, getKey) =>
    Array.from(items).reduce((result, item, index) => {
      const key = getKey(item, index)
      result[key] ??= []
      result[key].push(item)

      return result
    }, {})
}

if (!('groupBy' in Map)) {
  Map.groupBy = (items, getKey) =>
    Array.from(items).reduce((result, item, index) => {
      const key = getKey(item, index)

      if (!result.has(key)) result.set(key, [])

      result.get(key).push(item)

      return result
    }, new Map())
}

        
        
          
        
      

Если вы используете Typescript в своём проекте, убедитесь, что у вас версия 5.4 или новее. Начиная с неё в стандартных библиотеках есть типизация Object.groupBy() и Map.groupBy(). Подключить типизацию можно добавлением в tsconfig.json библиотек ESNext.Object и ESNext.Collection (для Object и Map соответственно):

        
          
            "compilerOptions": {-    "lib": ["DOM", "ES2020"],+    "lib": ["DOM", "ES2020", "ESNext.Object", "ESNext.Collection"],  }
            "compilerOptions": {
-    "lib": ["DOM", "ES2020"],
+    "lib": ["DOM", "ES2020", "ESNext.Object", "ESNext.Collection"],
  }