I have a List of objects, some of which may be similar to others based on a certain field. In this case, I want to filter one based on the value of another field.
Here is an example of what I have coming in:
[
{
employeeId: 1230,
firstName: "Ronald",
lastName: "McDonald",
device: "Laptop"
},
{
employeeId: 2345,
firstName: "Dennis",
lastName: "Reynolds",
device: "Laptop"
},
{
employeeId: 1230,
firstName: "Ronald",
lastName: "McDonald",
device: "Phone"
}
]
I have 2 objects for Ronald McDonald (employee 1230). I want the Laptop for all employees unless they have a Phone, in which case I just want the phone. So I want to filter out the Ronald McDonald object with device= "Laptop"
so that my output is as follows:
[
{
employeeId: 1230,
firstName: "Ronald",
lastName: "McDonald",
device: "Phone"
},
{
employeeId: 2345,
firstName: "Dennis",
lastName: "Reynolds",
device: "Laptop"
}
]
Filtering is easy enough, but for whatever reason I can't wrap my head around removing duplicates based on a condition.
Does any helpful soul have a solution to my situation?
I'm using Java 11, so I have access to Lambda functions, streams, all that good stuff.
Edit: My first crack at it is going terribly. I feel like I'm grouping ok, but I can't seem to filter within that group:
List<Employee> reducedEmpList = empList.stream()
.collect(Collectors.groupingBy(Employee::getEmployeeId,
// Obviously this is not right:
Collectors.maxBy(Comparator.comparing(Employee::getDevice, (e1, e2) -> {
// Clearly this is not how a comparator works
if("Laptop".equalsIgnoreCase(e1.getDevice()){
return e2;
}
}))));
CodePudding user response:
Iterate through all the employees, and if the device is Phone
search the list for the employee and add to the list entriesToRemove
if another entry exists with that employee and the device is Laptop
:
List<Employee> entriesToRemove = new ArrayList<>();
for(Employee employee : list){
if(employee.getDevice().equals("Phone")){
entriesToRemove.addAll(list.stream().filter(e ->
e.getEmployeeId() == employee.getEmployeeId() && e.getDevice().equals("Laptop"))
.collect(Collectors.toList()));
}
}
Finally remove the entries from the list with removeAll
:
list.removeAll(entriesToRemove);
System.out.printf(list.toString());
Output:
[Employee{employeeId=2345, device='Laptop'}, Employee{employeeId=1230, device='Phone'}]
CodePudding user response:
You can use the mergeFunction
of Collectors.toMap
to choose the one with "Phone" as the device when there's more than one:
Collection<Employee> filtered = employees.stream().collect(Collectors
.toMap(e -> e.getEmployeeId(), e -> e,
(a, b) -> "Phone".equals(a.getDevice())? a : b,
LinkedHashMap::new)).values();
employees = new ArrayList<>(filtered);
I specified a LinkedHashMap
as the intermediate storage so that any other employees (like Dennis) will remain in their relative positions, if that's important. If not, you can just use the 3 parameter overload of toMap
and leave that off.
Note: this assumes that if there is more than one entry for a given employee, one of them always has a device of "Phone".