Arindam
Arindam

Reputation: 723

How to find duplicate array list object and merge few fields with the unique key

Person elpidio = new Person.Builder().id(3L).firstName("elpidio").secondName("gomez").ext("121").build();

Person romual1 = new Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("141").build();

Person romual2 = new Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("144").build();

Now I need a out put some thing like this.

[Person [firstName=elpidio, secondName=gomez,ext=[121]],
 Person [firstName=romualdo, secondName=perez,ext=[121,144]]]

I refereed to below ans. But the problem I found is that I have many property in Person, and out of that I just need to merge one fields. So the entry set is giving issue. Any suggestion in welcome.

Extract duplicate objects from a List in Java 8

How to merge child objects in list based on duplicate parent object

Upvotes: 0

Views: 998

Answers (3)

Tushar Jajodia
Tushar Jajodia

Reputation: 451

There are a lot of ways to solve this question. However you if you want the answer as per your reference.

            persons.stream()
            .collect(Collectors.groupingBy(Function.identity(), toList()))
            .entrySet()
            .stream()
            .map(x -> {
                List<String> exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());
                Person duplicatePerson = x.getKey();
                duplicatePerson.setExField(exFields);
                return duplicatePerson;
            }).collect(toList());

Explanation. Here we are trying to group objects if certain fields are same in object. This can be done by overridinng the equals and HashCode method. like

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return Objects.equals(id, person.id) &&
            Objects.equals(firstName, person.firstName) &&
            Objects.equals(lastName, person.lastName);
}

@Override
public int hashCode() {
    return Objects.hash(id, firstName, lastName);
}

With this much in place you will be able to get your entrySet. Keys of the entrySet as the duplicate object (any object with those three fields) and value will be a list containing all those duplicate objects. Now from this duplicate list you only need that exFields from each object so this is the code

List<String> exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());

Why I had to use flatMap ?

Once you get the list of all the exFields you assign that list to your duplicate object.

 duplicatePerson.setExField(exFields);

Finally Collect.

Upvotes: 2

Gryphon
Gryphon

Reputation: 384

This is just one way to do it (and probably an overly convoluted, not-so-great way that I haven't completely checked for everything). What I like about this is that it shows how to make a collector of your own, and they are fairly useful to understand those underpinnings. Hopefully the comments are explanation enough. If not, there are lots of guides on custom collectors (like this)

Person elpidio = Person.Builder().id(3L).firstName("elpidio").secondName("gomez").ext("121").build();
Person romual1 = Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("141").build();
Person romual2 = Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("144").build();
Map<Long,Person> filteredPersons = Stream.of( elpidio, romual1, romual2 ).collect(Collector.of(
        HashMap::new,  // Starts with an empty list
        (result,entry) -> {
            if ( result.containsKey(entry.id)) {
                // If we already have this id in our map, we add the ext to the result list. This is assuming
                // "ext" in Person is a collection of some sort (because I don't feel like going through the
                // process of creating a new arbitrary class - in other words, that exercise is left for the
                // reader =) If you don't want dupes of ext, make sure it's a set or something else that will
                // remove dupes
                result.get(entry.id).ext.addAll(entry.ext);
            } else {
                result.put(entry.id, entry);
            }
        },
        (result1, result2) -> {
            // This handles the case where the collect is handled in parallel. How do we merge two resulting
            // maps?
            // In this case we are arbitrarily calling "result1" the version to check against, but it really
            // doesn't matter.
            for ( Map.Entry<Long,Person> person : result2.entrySet() ) {
                if ( result1.containsKey(person.getKey()) ) {
                    result1.get(person.getKey()).ext.addAll(person.getValue().ext);
                } else {
                    result1.put(person.getKey(), person.getValue());
                }
            }
            return result1;
        }));
filteredPersons.forEach( (k,v) -> System.out.println( k + " - first: " + v.first + " second: " + v.second + " " +
        "ext: " + v.ext));

outputs:

3 - first: elpidio second: gomez ext: [121]
4 - first: romualdo second: perez ext: [141, 144]

Upvotes: 2

Hilmi Reda
Hilmi Reda

Reputation: 104

You can use Java 8 Api Stream in order to filter them by id using a predicate or use distinct()

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

Upvotes: -1

Related Questions