User0911
User0911

Reputation: 1582

Java 8 streaming on Map

I have a map of map on which I want to stream,

Map<LocalDate, Map<Integer, List<String>>> mainMap;

and remove the values which match another map,

Map<LocalDate, List<String>> childMap;

Ex. mainMap contains

{2016-02-23={120=[1,2,3], 121=[1,2,3], 122=[1,2,3], 123=[1,2,3]}}

and childMap contains

{2016-02-23=[120,123]}

I want to remove these values from mainMap.

Expected Output =>

{2016-02-23={121=[1,2,3], 122=[1,2,3]}}

How to do it in Java 8?

Upvotes: 3

Views: 923

Answers (2)

Holger
Holger

Reputation: 298529

An alternative to Tunaki’s answer is

mainMap.forEach((k, v) -> childMap.getOrDefault(k, emptyList())
    .stream().map(Integer::valueOf).forEach(v::remove));

the difference is that Tunaki’s solution will iterate over the contents of the sub-maps contained in mainMap and perform a lookup on childMap for each, whereas the solution above will iterate over the lists found in childMap and perform a lookup the submaps of mainMap. This is preferable when the sizes of both, the submaps and the lists, grow.

One thing that hasn’t been addressed, is whether mappings of the mainMap should get removed if their submaps get emptied due to the operation. Supporting removal of outer mappings is not possible in one pass when iterating over the map that is going to be modified (unless you resort to an old loop using an Iterator).

A solution is to iterate over the childMap then:

childMap.forEach((k,v) -> mainMap.computeIfPresent(k, (d,m) -> {
    v.stream().map(Integer::valueOf).forEach(m::remove);
    return m.isEmpty()? null: m;
}));

Note that most complication arises from the type mismatch between your two maps, so a conversion between the childMap’s Strings to the mainMap’s Integers has to be performed. If they had the same type, e.g. if childMap was a Map<LocalDate, List<Integer>> or mainMap was Map<LocalDate, Map<String, List<String>>>, the solution not removing empty maps was as simple as

mainMap.forEach((k, v) -> v.keySet().removeAll(childMap.getOrDefault(k, emptyList())));

and the solution which will also remove empty mappings from the outer map:

childMap.forEach((k,v) -> mainMap.computeIfPresent(k, (d,m) -> {
    m.keySet().removeAll(v);
    return m.isEmpty()? null: m;
}));

The code above may remove mappings to an initially empty map. If you want to remove mappings only if the map has been emptied during this operation, i.e. not touch those which were empty to begin with, the code becomes even simpler:

childMap.forEach((k,v) ->
    mainMap.computeIfPresent(k, (d,m) -> m.keySet().removeAll(v) && m.isEmpty()? null: m));

Upvotes: 2

Tunaki
Tunaki

Reputation: 137259

You could do it in-place with the following:

mainMap.forEach((k, v) -> 
    v.keySet().removeIf(s -> 
        Optional.ofNullable(childMap.get(k)).map(o -> o.contains(s.toString())).orElse(false)
    )
);

This iterates through the mainMap. Then, concerning the value of that map, which is a Map<Integer, List<String>>, it removes all the integer keys (converted to a String) where the childMap for the current date points to a list containing that key. Note that the integer key is not removed if the child map does not contain a list for the current date.

A full sample code is the following, which prints your desired output:

public static void main(String[] args) {
    Map<LocalDate, Map<Integer, List<String>>> mainMap = new HashMap<>();

    Map<Integer, List<String>> map = new HashMap<>();
    map.put(120, Arrays.asList("1", "2", "3"));
    map.put(121, Arrays.asList("1", "2", "3"));
    map.put(122, Arrays.asList("1", "2", "3"));
    map.put(123, Arrays.asList("1", "2", "3"));
    mainMap.put(LocalDate.of(2016, 2, 23), map);

    Map<LocalDate, List<String>> childMap = new HashMap<>();
    childMap.put(LocalDate.of(2016, 2, 23), Arrays.asList("120", "123"));

    mainMap.forEach((k, v) -> 
        v.keySet().removeIf(s -> 
            Optional.ofNullable(childMap.get(k)).map(o -> o.contains(s.toString())).orElse(false)
        )
    );

    System.out.println(mainMap);
}

Upvotes: 4

Related Questions