usando lodash .groupBy. como adicionar suas próprias chaves para saída agrupada?

104

Eu tenho esses dados de amostra retornados de uma API.

Estou usando o Lodash _.groupBypara converter os dados em um objeto que posso usar melhor. Os dados brutos retornados são estes:

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

Quero que a _.groupByfunção retorne um objeto parecido com este:

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

Atualmente estou usando

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

que está retornando isso.

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

os agrupamentos estão corretos, mas eu realmente gostaria de adicionar as chaves que desejo ( color, users). isso é possível usando _.groupBy? ou algum outro LoDashutilitário?

user1767105
fonte

Respostas:

140

Você pode fazer assim no 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>


Resposta Original

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

Demonstração Online

Nota: Lodash 4.0 em diante, a .pairsfunção foi renomeada para_.toPairs()

os quatro olhos
fonte
Muito bacana, mas difícil de entender. Você pode explicar as etapas intermediárias, especialmente o emparelhamento e zip (e o zip duplo, já que _.objecté um apelido para _.zipObject).
Benny Bottema
3
lodash 3.10.0 e algum registro para cada etapa: jsfiddle.net/plantface/WYCF8/171 . Ainda é um quebra-cabeça, mas estou chegando lá. Não usei _.zipe _.pairmuito ainda.
Benny Bottema
12
No lodash 4.x, o pairs()método não existe mais. Uma versão lodash 4.0 atualizada deste exemplo é:.chain(data).groupBy('color') .toPairs().map(function (pair) { return _.zipObject(['Name', 'Suppliers'], air); }).value();
Erik Schierboom
5
Eu sinto que loadash deveria ter uma função que faça exatamente isso. Eu acho que os usuários gostariam de agrupar uma matriz de objetos por uma propriedade o tempo todo.
Adam Klein
1
Usar _.chainé considerado uma prática ruim agora.
Pawel
89

Não é tão simples?

var result = _(data)
            .groupBy(x => x.color)
            .map((value, key) => ({color: key, users: value}))
            .value();
Darragh
fonte
4
Isso parece muito mais limpo para mim e parece estar retornando os mesmos resultados. Obrigado!
Jeremy A. West
3
Uau. que sintaxe é essa?
Pela
Esta deve ser a resposta aceita. Simples e limpo.
Tiago Stapenhorst Martins
17

outra maneira

_.chain(data)
    .groupBy('color')
    .map((users, color) => ({ users, color }))
    .value();
Stasovlas
fonte
Semelhante à resposta de @ thefourtheye, mas um pouco mais fácil de entender
Martin Magakian
Você é meu herói
AlkanV
17

A resposta mais votada usa a função Lodash _.chain, que é considerada uma prática ruim agora "Por que usar _.chainé um erro."

Aqui estão algumas linhas que abordam o problema da perspectiva da programação funcional :

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)

Onde inputestá uma matriz que você deseja converter.

Pawel
fonte
Tentei usar em flow()vez de chain()aqui, mas a verificação de tipo do texto digitado simplesmente enlouquece com funções compostas. Espero que tenhamos o mesmo nível de suporte que temos atualmente em RxJS pipe(), por exemplo, em lodash.
BrunoJCM
Gosto muito da sua sintaxe map (), muito legal em map((users, color) => ({color, users}))vez demap((value, key) => ({ color: key, users: value }))
Gil Epshtain
7

Obrigado @thefourtheye , seu código ajudou muito. Criei uma função genérica da sua solução usando a versão 4.5.0 do Lodash.

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

Para usá-lo:

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

Aqui está o violinista atualizado;

https://jsfiddle.net/sc2L9dby/

Andrew
fonte
5

Aqui está uma versão atualizada usando lodash 4 e ES6

const result = _.chain(data)
    .groupBy("color")
    .toPairs()
    .map(pair => _.zipObject(['color', 'users'], pair))
    .value();
Mateus
fonte
2

Eu sugeriria uma abordagem diferente, usando minha própria biblioteca, você poderia fazer isso em algumas linhas:

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

var result = groupMe(collection);

Isso seria um pouco difícil com lodash ou sublinhado porque os argumentos estão na ordem oposta, então você teria que usar _.partialmuito.

elclanrs
fonte
2

Exemplo groupBy e soma de uma coluna usando 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);
Iyyappan Amirthalingam
fonte
legal ... e atualizado
Peter van der Lely
-2

Em 2017 faça isso

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