简体   繁体   中英

How can I filter a List of complex objects so that if two have value for a field, I remove one based on criteria

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;
      }
}))));

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());

Edit : Solution using Stream API:

List<Employee> reducedEmpList = list.stream()
   .collect(Collectors.groupingBy(Employee::getEmployeeId,
   Collectors.collectingAndThen(toList(), listPerEmployee ->
   listPerEmployee.stream().filter(e -> e.getDevice().equals("Phone")).findFirst()
     .map(Collections::singletonList).orElse(listPerEmployee))))
     .values().stream().flatMap(Collection::stream).collect(toList());

Output:

[Employee{employeeId=2345, device='Laptop'}, Employee{employeeId=1230, device='Phone'}]

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".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM