12

I have List<Person> where Person is as below.

class Person {

    String personId;
    LocalDate date;
    String type;

    // getters & setters

}

I'm trying to convert this to List<Person> to Map<String, Map<LocalDate,List<Person>>> where outer map's key is personId and inner map's key is date and I couldn't figure out how to achieve this. Thus far have tried something like below. Open to Java 8 solutions as well.

Map<String,Map<LocalDate,List<Person>>> outerMap = new HashMap<>();
Map<LocalDate,List<Person>> innerMap = new HashMap<>();

for(Person p : list) {
    List<Person> innerList = new ArrayList<>();
    innerList.add(p);
    innerMap.put(p.getDate(), innerList);
    outerMap.put(p.getPersonId(), innerMap);
}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Ram
  • 1,743
  • 2
  • 18
  • 40
  • You should explain your trail of thought. Why is `innerList` declared inside the loop? Why `innerMap` declared outside? Why `outerMap` is declared outside as well? – M. Prokhorov Apr 12 '18 at 14:48
  • Possible duplicate of [Convert a Nested List to nested Map](https://stackoverflow.com/questions/48379078/convert-a-nested-list-to-nested-map) – Hadi J Apr 12 '18 at 14:55

3 Answers3

17
list.stream()
    .collect(Collectors.groupingBy(
        Person::getPersonId,
        Collectors.groupingBy(
            Person::getDate
)));
Eugene
  • 117,005
  • 15
  • 201
  • 306
7

This answer shows how to do it with streams and this other one how to do it with a traditional iterative approach. Here's yet another way:

Map<String, Map<LocalDate, List<Person>>> outerMap = new HashMap<>();
list.forEach(p -> outerMap
        .computeIfAbsent(p.getPersonId(), k -> new HashMap<>()) // returns innerMap
        .computeIfAbsent(p.getDate(), k -> new ArrayList<>())   // returns innerList
    .add(p)); // adds Person to innerList

This uses Map.computeIfAbsent to create a new innerMap and put it in the outerMap if not present (the key being personId), and also to create a new innerList and put it in the innerMap if not present (the key being date). Finally, the Person is added to the innerList.

fps
  • 33,623
  • 8
  • 55
  • 110
3

Your for loop organization should be changed in such a way that it tries getting an existing List<Person> from the nested map before proceeding with creation of the new list. This is a two-step process:

Map<String,Map<LocalDate,List<Person>>> outerMap = new HashMap<>();
for(Person p : list) {
    Map<LocalDate,List<Person>> innerMap = outerMap.get(p.getPersonId());
    if (innerMap == null) {
        innerMap = new HashMap<>();
        outerMap.put(p.getPersonId(), innerMap);
    }
    List<Person> innerList = innerMap.get(p.getDate());
    if (innerList == null) {
        innerList = new ArrayList<>();
        innerMap.put(p.getDate(), innerList);
    }
    innerList.add(p);
}
M. Prokhorov
  • 3,894
  • 25
  • 39
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523