Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete entries of a Map<Long, List<Object>>

after hours of research I can't find the solution of my problem. I have a Map<String, List<User>> and I want to remove some items of the Map based on some values in the List.

I have to following User class

public class User {
    public String firstName,
    public Integer age

    public boolean is50() {
       return this.age.equals(50);
    }

}

And I have a Map as this :

Map<String, List<User> myMap = new HashMap<>();

myMap.put("A", new ArrayList<>());
myMap.get("A").add(new User("John", 50));
myMap.get("A").add(new User("Bob", 30));

myMap.put("B", new ArrayList<>());
myMap.get("B").add(new User("Sam", 25));
myMap.get("B").add(new User("Sarah", 56));

myMap.put("C", new ArrayList<>());
myMap.get("C").add(new User("Gill", 15));
myMap.get("C").add(new User("Jim", 20));

Now, I want to remove entries of Map if at least One user in a List age is 50. Also, I want to use Java 8 features to achieve this.

I've found that there is a removeIf function but I can't make it works with Lists.

I've tried something like this :

Map<String, List<User> filteredMap = myMap
                                 .enrySet()
                                 .removeIf(e -> e.getValue()
                                        .stream()
                                        .filter(user -> user::is50)
                                        .collect(Collectors.toList())

Of course that doesn't work :(

The excepted Output is a filteredMap with only B et C keys with all users (the A key must be deleted because John has 50 yrs old)

Thx for your help and hope it is clear ;)

like image 945
Pierre Jones Avatar asked Aug 20 '19 13:08

Pierre Jones


1 Answers

This should do the trick:

myMap.entrySet()
    .removeIf(e -> e.getValue()
        .stream()
        .anyMatch(User::is50));

Do note that entrySet() modifications (removeIf) affect the map backed by the entry set. So, you either have to make a copy of the map and run this on the copy, or let the first map (myMap) be modified.

As for making copies of your map, you can simply do:

Map<String, List<User>> copy = new HashMap<>(myMap);

EDIT:
As per @Holger's suggestion, the better way to do in-place removal is as following:

myMap.values()
    .removeIf(v -> v.stream()
        .anyMatch(User::is50));

The reason this is better is fourfold:

  1. You can eliminate a call to getValue since values provides the value set directly.
  2. It communicates intent more clearly - there is less of a logical leap between values and a value than between entrySet and a value.
  3. It's more concise and is not an anti-pattern (e.g. the use of peek outside of debugging is an anti-pattern, despite sometimes being more concise).
  4. It still works, since the Collection returned by values is backed by the map.
like image 116
Avi Avatar answered Nov 28 '22 21:11

Avi