Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Flatten nested map using Stream

I have been messing around with Java 8 Stream API and ran into something I could only do through traditional for-loops.

Given a nested map of

{
    1999: {
             3: [23, 24, 25],
             4: [1, 2, 3]
          },
    2001: {
             11: [12, 13, 14],
             12: [25, 26, 27]

          }
}

How can I transform this into

['23,3,1999', '24,3,1999', '25,3,1999', '1,4,1999', '2,4,1999', '3,4,1999', '12,11,2001', '13,11,2001', '14,11,2001', '25,12,2001', '26,12,2001', '27,12,2001']

Basically I want to replicate:

    Map<Integer, Map<Integer, List<Integer>>> dates...
    List<String> flattened = new ArrayList<>();
    for (Integer key1 : map.keySet()) {
        for (Integer key2 : map.get(key1).keySet()) {
            for (Integer value : map.get(key1).get(key2)) {
                flattened.add(value + "," + key2 + "," + key1);
            }
        }
    }
like image 202
knc Avatar asked Mar 29 '17 07:03

knc


3 Answers

Try this for example:

public static void main(String[] args) {
    Map<Integer, Map<Integer, List<Integer>>> dates =
        new HashMap<Integer, Map<Integer, List<Integer>>>() {{
            put(1999, new HashMap<Integer, List<Integer>>() {{
                put(3, Arrays.asList(23, 24, 25));
                put(4, Arrays.asList(1, 2, 3));
            }});
            put(2001, new HashMap<Integer, List<Integer>>() {{
                    put(11, Arrays.asList(12, 13, 14));
                    put(12, Arrays.asList(25, 26, 27));
            }});
        }};

    dates.entrySet().stream().flatMap(year ->
        year.getValue().entrySet().stream().flatMap(month ->
            month.getValue().stream()
                .map(day -> day + "," + month.getKey() + "," + year.getKey())))
        .forEach(System.out::println);
}

and if you need them sorted by year then month then day try this:

    dates.entrySet().stream().flatMap(year ->
        year.getValue().entrySet().stream().flatMap(month ->
            month.getValue().stream()
                .map(day -> Arrays.asList(year.getKey(), month.getKey(), day))))
        .sorted(Comparator.comparing(l -> l.get(0)*10000 + l.get(1)*100 + l.get(2)))
        .forEach(System.out::println);
like image 104
Harmlezz Avatar answered Oct 09 '22 05:10

Harmlezz


 items
            .entrySet()
            .stream()
            .flatMap(
                    l-> l.getValue().entrySet().stream()
                            .flatMap(
                                    ll->ll.getValue().stream()
                                         .map(lll+","+ll.getKey()+","+l.getKey())))
            .collect(Collectors.toList())
            .forEach(System.out::println);
like image 5
Boris Zhguchev Avatar answered Oct 09 '22 05:10

Boris Zhguchev


Assuming there is a Holder that can take 3 integers for example:

 static class Holder {

      private final int first;
      private final int second;
      private final int third;
 }

And these are some nested maps, then 2 flatMap will achieve what you want:

 List<Holder> holders = map.entrySet().stream().flatMap(
            e -> e.getValue()
                    .entrySet()
                    .stream()
                    .flatMap(x -> x.getValue().stream().map(y -> new Holder(e.getKey(), x.getKey(), y))))
            .collect(Collectors.toList());

    System.out.println(holders);
like image 2
Eugene Avatar answered Oct 09 '22 03:10

Eugene