используя lodash .groupBy. как добавить свои собственные ключи для сгруппированного вывода?

105

У меня есть эти образцы данных, возвращенные из API.

Я использую Lodash _.groupByдля преобразования данных в объект, который я могу использовать лучше. Возвращенные необработанные данные:

[
    {
        "name": "jim",
        "color": "blue",
        "age": "22"
    },
    {
        "name": "Sam",
        "color": "blue",
        "age": "33"
    },
    {
        "name": "eddie",
        "color": "green",
        "age": "77"
    }
]

Я хочу, чтобы _.groupByфункция возвращала объект, который выглядит так:

[
    {
        color: "blue",
        users: [
            {
                "name": "jim",
                "color": "blue",
                "age": "22"
            },
            {
                "name": "Sam",
                "color": "blue",
                "age": "33"
            }
        ]
    },
    {
        color: "green",
        users: [
            {
                "name": "eddie",
                "color": "green",
                "age": "77"
            }
        ]
    }
]

В настоящее время я использую

_.groupBy(a, function(b) { return b.color})

который возвращает это.

{blue: [{..}], green: [{...}]}

группировки правильные, но я бы очень хотел добавить нужные мне ключи ( color, users). это возможно с помощью _.groupBy? или какая-то другая LoDashутилита?

user1767105
источник

Ответы:

144

Вы можете сделать это в Lodash 4.x

var data = [{
  "name": "jim",
  "color": "blue",
  "age": "22"
}, {
  "name": "Sam",
  "color": "blue",
  "age": "33"
}, {
  "name": "eddie",
  "color": "green",
  "age": "77"
}];

console.log(
  _.chain(data)
    // Group the elements of Array based on `color` property
    .groupBy("color")
    // `key` is group's name (color), `value` is the array of objects
    .map((value, key) => ({ color: key, users: value }))
    .value()
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


Оригинальный ответ

var result = _.chain(data)
    .groupBy("color")
    .pairs()
    .map(function(currentItem) {
        return _.object(_.zip(["color", "users"], currentItem));
    })
    .value();
console.log(result);

Онлайн-демонстрация

Примечание. Начиная с Lodash 4.0 .pairsфункция была переименована в_.toPairs()

четыре глаза
источник
Очень изящно, но сложно осмыслить. Можете ли вы объяснить шаги между ними, особенно сопряжение и застегивание (и двойную застежку-молнию, поскольку _.objectэто псевдоним _.zipObject).
Benny Bottema
3
lodash 3.10.0 и ведение журнала для каждого шага: jsfiddle.net/plantface/WYCF8/171 . Это все еще загадка, но я добираюсь до цели. Еще не использовал _.zipи _.pairтак много.
Бенни Боттема
12
В lodash 4.x pairs()метода больше не существует. Обновленная версия этого примера lodash 4.0:.chain(data).groupBy('color') .toPairs().map(function (pair) { return _.zipObject(['Name', 'Suppliers'], air); }).value();
Эрик Ширбум
5
Я чувствую, что у loadash должна быть функция, которая делает это очень точно. Я бы подумал, что пользователи хотят постоянно группировать массив объектов по свойству.
Адам Кляйн
1
Использование _.chainсейчас считается плохой практикой.
Павел
90

Разве это не так просто?

var result = _(data)
            .groupBy(x => x.color)
            .map((value, key) => ({color: key, users: value}))
            .value();
Дарра
источник
4
Мне это кажется намного чище и, похоже, возвращает те же результаты. Спасибо!
Джереми А. Вест,
3
Вот это да. что это за синтаксис? Впервые увидел, что вы можете передать массив в конструктор
Wei-jye
Это должен быть принятый ответ. Просто и чисто.
Тьяго Стапенхорст Мартинс,
17

по-другому

_.chain(data)
    .groupBy('color')
    .map((users, color) => ({ users, color }))
    .value();
Стасовлас
источник
Подобно ответу @ thefourtheye, но немного легче для понимания
Мартин Магакян
Ты мой герой
AlkanV
17

Ответ с наибольшим количеством_.chain голосов использует функцию Lodash, которая сейчас считается плохой практикой: «Почему использование _.chainявляется ошибкой».

Вот несколько подходов к проблеме с точки зрения функционального программирования :

import tap from "lodash/fp/tap";
import flow from "lodash/fp/flow";
import groupBy from "lodash/fp/groupBy";

const map = require('lodash/fp/map').convert({ 'cap': false });

const result = flow(
      groupBy('color'),
      map((users, color) => ({color, users})),
      tap(console.log)
    )(input)

Где inputнаходится массив, который вы хотите преобразовать.

Павел
источник
Я пытался использовать flow()вместо chain()here, но проверка типов машинописного текста просто сходила с ума от составных функций. Я надеюсь, что у нас есть тот же уровень поддержки, что и в RxJS pipe(), например, в lodash.
BrunoJCM
Мне очень нравится ваш синтаксис map (), map((users, color) => ({color, users}))вместо этого очень крутоmap((value, key) => ({ color: key, users: value }))
Гил Эпштейн
7

Спасибо @thefourtheye , ваш код очень помог. Я создал универсальную функцию из вашего решения, используя Lodash версии 4.5.0.

function groupBy(dataToGroupOn, fieldNameToGroupOn, fieldNameForGroupName, fieldNameForChildren) {
            var result = _.chain(dataToGroupOn)
             .groupBy(fieldNameToGroupOn)
             .toPairs()
             .map(function (currentItem) {
                 return _.zipObject([fieldNameForGroupName, fieldNameForChildren], currentItem);
             })
             .value();
            return result;
        }

Чтобы использовать это:

var result = groupBy(data, 'color', 'colorId', 'users');

Вот обновленный скрипач;

https://jsfiddle.net/sc2L9dby/

Андрей
источник
5

Вот обновленная версия с использованием lodash 4 и ES6

const result = _.chain(data)
    .groupBy("color")
    .toPairs()
    .map(pair => _.zipObject(['color', 'users'], pair))
    .value();
Мэтью
источник
2

Я бы предложил другой подход, используя мою собственную библиотеку, вы могли бы сделать это в несколько строк:

var groupMe = sequence(
  groupBy(pluck('color')),
  forOwn(function(acc, k, v) {
    acc.push({colors: k, users: v});
    return acc;
  },[])
);

var result = groupMe(collection);

Это было бы немного сложно с lodash или Underscore, потому что аргументы находятся в обратном порядке, поэтому вам придется использовать _.partialмного.

Elclanrs
источник
2

Пример groupBy и суммы столбца с использованием Lodash 4.17.4

   var data = [{
                "name": "jim",
                "color": "blue",
                "amount": 22
                }, {
                "name": "Sam",
                "color": "blue",
                "amount": 33
                }, {
               "name": "eddie",
               "color": "green",
               "amount": 77
              }];

      var result = _(data)
                   .groupBy(x => x.color)
                   .map((value, key) => 
                   ({color: key,
                    totalamount: _.sumBy(value,'amount'),
                    users: value})).value();

                    console.log(result);
Ийяппан Амиртхалингам
источник
красиво ... и в курсе
Питер ван дер Лели
-2

В 2017 так и сделаю

_.chain(data)
  .groupBy("color")
  .toPairs()
  .map(item => _.zipObject(["color", "users"], item))
  .value();
Владислав Сироштан
источник