usando lodash .groupBy. ¿Cómo agregar sus propias claves para la salida agrupada?

104

Tengo estos datos de muestra devueltos desde una API.

Estoy usando Lodash's _.groupBypara convertir los datos en un objeto que puedo usar mejor. Los datos brutos devueltos son los siguientes:

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

Quiero que la _.groupByfunción devuelva un objeto que se parece a esto:

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

Actualmente estoy usando

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

que está devolviendo esto.

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

las agrupaciones son correctas, pero realmente me gustaría agregar las claves que quiero ( color, users). ¿Es esto posible el uso _.groupBy? o alguna otra LoDashutilidad?

usuario1767105
fuente

Respuestas:

140

Puedes hacerlo así en 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>


Respuesta original

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

Demo en línea

Nota: Lodash 4.0 en adelante, la .pairsfunción ha sido renombrada a_.toPairs()

los cuatro ojos
fuente
Muy ingenioso, pero difícil de entender. ¿Puede explicar los pasos intermedios, especialmente el emparejamiento y la cremallera (y la cremallera doble, ya que _.objectes un alias de _.zipObject)?
Benny Bottema
3
lodash 3.10.0 y algunos registros para cada paso: jsfiddle.net/plantface/WYCF8/171 . Sigue siendo un rompecabezas, pero estoy llegando. No he usado _.zipy _.pairtanto todavía.
Benny Bottema
12
En lodash 4.x, el pairs()método ya no existe. Una versión actualizada de lodash 4.0 de este ejemplo es:.chain(data).groupBy('color') .toPairs().map(function (pair) { return _.zipObject(['Name', 'Suppliers'], air); }).value();
Erik Schierboom
5
Siento que loadash debería tener una función que haga exactamente esto. Creo que los usuarios querrían agrupar una matriz de objetos por una propiedad todo el tiempo.
Adam Klein
1
El uso _.chainse considera ahora una mala práctica.
Pawel
89

¿No es así de simple?

var result = _(data)
            .groupBy(x => x.color)
            .map((value, key) => ({color: key, users: value}))
            .value();
Darragh
fuente
4
Esto me parece mucho más limpio y parece estar dando los mismos resultados. ¡Gracias!
Jeremy A. West
3
Guau. que sintaxis es esta? Primera vez que
veo
Esta debería ser la respuesta aceptada. Simple y limpio.
Tiago Stapenhorst Martins
17

de otra manera

_.chain(data)
    .groupBy('color')
    .map((users, color) => ({ users, color }))
    .value();
Stasovlas
fuente
Similar a la respuesta de @ thefourtheye pero un poco más fácil de entender
Martin Magakian
Eres mi héroe
AlkanV
17

La respuesta más votada usa la función Lodash _.chain, que ahora se considera una mala práctica "Por qué usar _.chaines un error".

Aquí hay algunos lineamientos que abordan el problema desde la perspectiva de la programación 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)

¿Dónde inputhay una matriz que desea convertir?

Pawel
fuente
Traté de usar en flow()lugar de chain()aquí, pero la verificación de tipo de mecanografiado simplemente se vuelve loca con las funciones compuestas. Espero que tengamos el mismo nivel de soporte que tenemos actualmente en RxJS pipe(), por ejemplo, en lodash.
BrunoJCM
Realmente me gusta su sintaxis map (), muy genial en map((users, color) => ({color, users}))lugar demap((value, key) => ({ color: key, users: value }))
Gil Epshtain
7

Gracias @thefourtheye , su código ayudó mucho. Creé una función genérica a partir de su solución usando la versión 4.5.0 de 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 usarlo:

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

Aquí está el violinista actualizado;

https://jsfiddle.net/sc2L9dby/

Andrés
fuente
5

Aquí hay una versión actualizada usando lodash 4 y ES6

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

Sugeriría un enfoque diferente, usando mi propia biblioteca , podría hacer esto en unas pocas líneas:

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

var result = groupMe(collection);

Esto sería un poco difícil con lodash o subrayado porque los argumentos están en el orden opuesto, por lo que tendrías que usar _.partialmucho.

elclanrs
fuente
2

Ejemplo groupBy y suma de una columna 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
fuente
agradable ... y actualizado
Peter van der Lely
-2

En 2017 hazlo

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