Devs
Devs

Reputation: 49

Find duplicates and remove from ArrayList

I have an ArrayList of Device objects. I want to remove duplicate entries.

The device objects has three fields: id, name, and status. If an id occurs multiple times, I need to check the status of each duplicate id and filter it based on the below criteria.

There can be 3 status i.e. New, Pending, Complete.

  1. If a duplicate id has a status as Complete then keep that object and remove all other object for this id having any other status.
  2. If duplicate id does not have a status as Complete then check for status Pending and keep that and remove any other occurrence for that id in the list.

Device class -:

class Device {
    long id
    String name
    String status

    @Override
    String toString() {
        return "Device [${Id}, ${status}]"
    }
}

Sample data:

[
    [id : 11, name:'test', status:'pending'],
    [id : 13, name : 'abc', status:'new'],
    [id : 14, name : 'xyz', status:'pending'],
    [id : 11, name : 'test', status:'new'],
    [id : 15, name : 'wxy', status:'complete'],
    [id : 15, name : 'wxy', status:'pending']
]

Expected output:

[
    [id : 11, name:'test', status:'pending'],
    [id : 13, name : 'abc', status:'new'],
    [id : 14, name : 'xyz', status:'pending'],
    [id : 15, name : 'wxy', status:'complete'],
]

Can someone here help with this logic possibly in Groovy or else in Java?

Upvotes: 1

Views: 350

Answers (2)

injecteer
injecteer

Reputation: 20699

I would use Groovy's groupBy/sort combo to achieve what you want:

class Device {

    long id
    String name
    String status

    @Override
    String toString() { "Device [$id, $status]" }
}

def devices = [
    [id : 11, name:'test', status:'pending'],
    [id : 13, name : 'abc', status:'new'],
    [id : 14, name : 'xyz', status:'pending'],
    [id : 11, name : 'test', status:'new'],
    [id : 15, name : 'wxy', status:'complete'],
    [id : 15, name : 'wxy', status:'pending']
].collect{ it as Device }

def result = devices.groupBy{ it.id }.findResults{ id, devs ->
    devs.sort{ [ 'complete', 'pending', 'new' ].indexOf it.status }.first()
}

assert result.toString() == '[Device [11, pending], Device [13, new], Device [14, pending], Device [15, complete]]'

Upvotes: 1

Nowhere Man
Nowhere Man

Reputation: 19545

This may be resolved by building a map by device.id and implementing the merge function with the custom logic.

The device statuses may be ordered using an enum/map, so the device with "higher" status is kept while merging.

Map<String, Integer> statusOrder = Map.of(
    "new", 1,
    "pending", 2,
    "complete", 3
);

List<Device> devices = Arrays.asList(
    new Device(11, "test", "pending"),
    new Device(13, "abc", "new"),
    new Device(14, "xyz", "pending"),
    new Device(11, "test", "new"),
    new Device(15, "wxy", "complete"),
    new Device(15, "wxy", "pending")
);

devices
    .stream()
    .collect(Collectors.toMap(
        Device::getId,
        d -> d,
        (d1, d2) -> statusOrder.get(d1.status) 
                 >= statusOrder.get(d2.status) ? d1 : d2,
            LinkedHashMap::new
        ))
        .values()
        .forEach(System.out::println);

Output:

Device [11, test, pending]
Device [13, abc, new]
Device [14, xyz, pending]
Device [15, wxy, complete]

Upvotes: 1

Related Questions