I have Employee
object that has 3
fields: name
, role
, companyName
.
I need to filter Employee
objects based on a role
property.
Despite that I'm applying filter
in the stream in the code below, it's returning the entire data.
My code:
public static Map<String, List<? extends Employee>> testMappingFilter(List<Employee> emp) {
Map<String, List<? extends Employee>> dataInput = new HashMap<>();
for (Employee entry : emp) {
dataInput.put("senior", new ArrayList(emp));
dataInput.put("junior", new ArrayList(emp));
}
Map<String, List<? extends Employee>> dataResponse = new HashMap<>();
Set<String> realmroles = new HashSet<>();
realmroles.add("admin");
realmroles.add("user");
Either<Map<String, List<? extends Employee>>, ResponseFormat> followedResourcesServices = Either.left(dataInput);
realmroles.stream().forEach(role ->
followedResourcesServices.left().value().entrySet().stream()
.filter(elements -> elements.getValue().stream().anyMatch(i -> ((Employee) i).getRole().contains(role)))
.forEach(elements -> dataResponse.put(elements.getKey(), elements.getValue())));
return dataResponse;
}
Sample data:
Expected Output:
(expected roles are only "admin"
and "user"
, data related to role "super"
should not come)
{ “senior” :
[ { ”name”:”ABC”, “role”:”admin”, “companyName”:”ABC &Co” },
{ ”name”:”XYZ”, “role”:”admin”, “companyName”:”XYZ &Co” },
{ ”name”:”RST”, “role”:”user”, “companyName”:”RST &Co” } ], “junior” :
[ { ”name”:”ABC”, “role”:”admin”, “companyName”:”ABC &Co” },
{ ”name”:”XYZ”, “role”:”admin”, “companyName”:”XYZ &Co” },
{ ”name”:”RST”, “role”:”user”, “companyName”:”RST &Co” } ]
}
Actual output:
{ “senior” :
[ { ”name”:”ABC”, “role”:”admin”, “companyName”:”ABC &Co” },
{ ”name”:”DDD”, “role”:”super”, “companyName”:”DDD &Co” },
{ ”name”:”XYZ”, “role”:”admin”, “companyName”:”XYZ &Co” },
{ ”name”:”RST”, “role”:”user”, “companyName”:”RST &Co” } ], “junior” :
[ { ”name”:”ABC”, “role”:”admin”, “companyName”:”ABC &Co” },
{ ”name”:”DDD”, “role”:”super”, “companyName”:”DDD &Co” },
{ ”name”:”XYZ”, “role”:”admin”, “companyName”:”XYZ &Co” },
{ ”name”:”RST”, “role”:”user”, “companyName”:”RST &Co” } ]
}
CodePudding user response:
There are several issues in your code:
The filtering logic is incorrect: you said you need to retain only roles
"admin"
and"user"
, but according to your code anyList
ofEmployee
that contains at least one employee with either of these roles is good to go (meaning the whole list, not only employee having the required role). That's why you see roles”super”
in your output. You need to filter the employees in each list, instead of filtering the lists.That's not a job for
Stream.forEach
, which operates via side-effects. It's discouraged to be used for in place of reduction operations. Have a look at the API documentation, especially pay attention to the code examples.Instead of iterating over the
Set
of roles, we can simply usecontains()
check, and iterate only over the entries of the map which you're retrieving by callingEither.left(dataInput).value()
.
To accumulate the values from the stream we have a wide range of operations, and the most suitable of them in this case is collect()
, because we need to perform mutable reduction.
We can't apply the filtering logic inside the stream without creating new objects (which costs performance). Another option we have is to perform filtering inside a Collector
while accumulating the data.
I'll with the second option. In order to generate the resulting map, we can use collector groupingBy()
in conjunction with flatMapping()
toList()
applied as the downstream.
Map<String, List<? extends Employee>> dataInput = // initializing dataInput
Set<String> realmroles = // initializing realmroles
return Either.left().value().entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.flatMapping(entry -> entry.getValue().stream()
.filter(emp -> realmroles.contains(((Employee) emp).getRole())),
Collectors.toList())
));
CodePudding user response:
You have to set the value (emp list) in your map.
followedResourcesServices.left().value().entrySet().stream().forEach(e -> {
e.setValue(e.getValue().stream().filter(i -> realmroles.stream().anyMatch(role -> ((Employee) i).getRole().equals(role))).
collect(Collectors.toList()));
dataResponse.put(e.getKey(), e.getValue());
});