@Data public class Employee{ private int empid; private String empPFcode; private String collegeName; } Employee emp1=new Employee (1334090,"220","AB"); Employee emp2=new Employee (1334091,"220","AB"); Employee emp3=new Employee (1334092,"220","AC"); Employee emp4=new Employee (1434091,"221","DP"); Employee emp5=new Employee (1434091,"221","DP"); Employee emp6=new Employee (1434092,"221","DP");
I want to filter this Employee
object based on the EmpPFcode
. If collegeName
has common value for 3 EmpPFcode
, we will collect otherwise we will skip that records.
So my result would be like below.
Employee emp4=new Employee (1434091,"221","DP"); Employee emp5=new Employee (1434091,"221","DP"); Employee emp6=new Employee (1434092,"221","DP");
Below one will skip because collageName
is different.
I try to do some logic below but it doesn’t not filter properly.
List<CombinedDTO> distinctElements = list.stream().filter(distinctByKeys(Employee ::empPFcode,Employee ::collegeName)) .collect(Collectors.toList()); public static <T> Predicate <T> distinctByKeys(Function<? super T, Object>... keyExtractors) { Map<Object, Boolean> uniqueMap = new ConcurrentHashMap<>(); return t -> { final List<?> keys = Arrays.stream(keyExtractors) .map(ke -> ke.apply(t)) .collect(Collectors.toList()); return uniqueMap.putIfAbsent(keys, Boolean.TRUE) == null; }; }
Advertisement
Answer
I. Solution:
A more cleaner and readable solution would be to have a set of empPFcode
values ([221]
), then filter the employee list only by this set.
First you can use Collectors.groupingBy()
to group by empPFcode
, then you can use Collectors.mapping(Employee::getCollegeName, Collectors.toSet())
to get a set of collegeName
values.
Map<String, Set<String>> pairMap = list.stream().collect(Collectors.groupingBy(Employee::getEmpPFcode, Collectors.mapping(Employee::getCollegeName, Collectors.toSet())));
will result in: {220=[AB, AC], 221=[DP]}
Then you can remove the entries which includes more than one collegeName
:
pairMap.values().removeIf(v -> v.size() > 1);
will result in: {221=[DP]}
The last step is filtering the employee list by the key set. You can use java.util.Set.contains()
method inside the filter:
List<Employee> distinctElements = list.stream().filter(emp -> pairMap.keySet().contains(emp.getEmpPFcode())) .collect(Collectors.toList());
II. Solution:
If you use Collectors.groupingBy()
nested you’ll get a Map<String,Map<String,List<Employee>>>
:
{ 220 = {AB=[...], AC=[...]}, 221 = {DP=[...]} }
Then you can filter by the map size (Map<String,List<Employee>>
) to eliminate the entries which has more than one map in their values (AB=[...], AC=[...]
).
You still have a Map<String,Map<String,List<Employee>>>
and you only need List<Employee>
. To extract the employee list from the nested map, you can use flatMap()
.
Try this:
List<Employee> distinctElements = list.stream() .collect(Collectors.groupingBy(Employee::getEmpPFcode, Collectors.groupingBy(Employee::getCollegeName))) .entrySet().stream().filter(e -> e.getValue().size() == 1).flatMap(m -> m.getValue().values().stream()) .flatMap(List::stream).collect(Collectors.toList());