Agrupación por y valor de mapa

3 minutos de lectura

avatar de usuario de doublemc
doblemc

Estoy tratando de agrupar por (para mapear) y luego transformar la lista de valores en una lista diferente.

Tengo una lista de documentos del distrito:

List<DistrictDocument> docs = new ArrayList<>();

Luego lo transmito y lo agrupo por ciudad:

Map<String, List<DistrictDocument>> collect = docs.stream()
                .collect(Collectors.groupingBy(DistrictDocument::getCity));

También tengo un método que toma DistrictDocument y crea Slugable a partir de él:

private Fizz createFizz(DistrictDocument doc) {
    return new Fizz().name(doc.getName()).fizz(doc.getFizz());
}

¿Hay alguna manera de poner ese método en mi transmisión anterior para obtener Map<String, List<Fizz>> ? Intenté agregar el segundo argumento a groupingBy pero no pude encontrar una forma adecuada y siempre recibía errores de compilación.

Editar: ¿Qué pasa si mi createFizz regresa? List<Fizz> ? ¿Hay alguna opción para aplanar esta lista en Collectors.mapping porque todavía quiero tener Map<String, List<Fizz>> en lugar de Map<String, List<List<Fizz>>>

Avatar de usuario de Eran
Eran

Tienes que encadenar un Collectors.mapping() coleccionista a la Collectors.groupingBy() coleccionista:

Map<String, List<Fizz>> collect =
    docs.stream()
        .collect(Collectors.groupingBy(DistrictDocument::getCity,
                 Collectors.mapping(d->createFizz(d),Collectors.toList())));

Si createFizz(d) devolvería un List<Fizzpuede aplanarlo usando Java 9 Collectors.flatMapping:

Map<String, List<Fizz>> collect =
    docs.stream()
        .collect(Collectors.groupingBy(DistrictDocument::getCity,
                 Collectors.flatMapping(d->createFizz(d).stream(),Collectors.toList())));

Si no puede usar Java 9, tal vez usando Collectors.toMap ayudará:

Map<String, List<Fizz>> collect =
    docs.stream()
        .collect(Collectors.toMap(DistrictDocument::getCity,
                                  d->createFizz(d),
                                  (l1,l2)->{l1.addAll(l2);return l1;}));

  • d->createFizz(d) Puede usar una referencia de método para esto. Es cierto que no conocemos la clase.

    – Miguel

    28 de febrero de 2018 a las 12:25

  • @Michael sí, no saber la clase fue la razón principal por la que no usé una referencia de método.

    – Eran

    28 de febrero de 2018 a las 12:26

  • ¿Podrías revisar mi edición? Desafortunadamente, createFizz tiene que devolver List y parece que no puedo encontrar una manera de aplanar esta lista.

    – doblemc

    28 de febrero de 2018 a las 12:40

  • @doublemc Creo que java 9 tiene Collectors.flatMapping, así que puedes escribir Collectors.flatMapping(d->createFizz(d).stream(),Collectors.toList())));

    – Eran

    28 de febrero de 2018 a las 12:41

  • ¿Qué pasa si solo puedo usar hasta Java 8? ¿Alguna opción para hacer eso?

    – doblemc

    28 de febrero de 2018 a las 12:43

En caso de que desee hacer una agrupación doblemente anidada:

Digamos que tienes una colección de EducationData objetos que contienen el nombre de la escuela, el nombre del profesor y el nombre del alumno. Pero quieres un mapa anidado que se parezca a:

Que tienes

class EducationData {
     String school;
     String teacher;
     String student;

     // getters setters ...
}

Lo que quieras

Map<String, Map<String, List<String>>> desiredMapOfMpas ...

// which would look like this :

"East High School" : {
     "Ms. Jackson" : ["Derek Shepherd", "Meredith Grey", ...],
     "Mr. Teresa" : ["Eleanor Shellstrop", "Jason Mendoza", ...],
     ....
}

Cómo llegar allá

import static java.util.stream.Collectors.*;

public doubleNestedGroup(List<EducationData> educations) {
     Map<String, Map<String, List<String>>> nestedMap = educations.stream()
        .collect(
             groupingBy(
                  EducationData::getSchool,
                  groupingBy(
                       EducationData::getTeacher,
                       mapping(
                            EducationData::getStudent,
                            toList()
                       )
                  )
              )
        );
}

¿Ha sido útil esta solución?