I'm new into programming and now facing a tricky problem: I got an Object which contains a list, so the Object looks like the following:
public class SampleClass {
private UUID id;
private List<ValueObject> values;
// getters, constructor, etc
}
The ValueObject
contains some Strings like:
public class ValueObject {
private String firstName;
private String lastName;
private String address;
// getters, constructor, etc
}
I have a SampleClass
intance which contains a list of multiple ValueObject
s. Some of the ValueObject
s have the same firstName
and lastName
.
What I want to archive is that I want to filter out all ValueObject
within a SampleClass
object having the same firstName
and lastName
. And I want to keep the last (according to the encounter order) ValueObject
out of each group duplicates in the list.
I've tried the following:
SampleClass listWithDuplicates = // intializing SampleClass instance
listWithDuplicates.getValues().stream()
.collect(Collectors.groupingBy(
ValueObject::getLastname,
Collectors.toList()
));
To group it by lastname but how do I find then the matching firstNames, because lastname can be equal but firstname can be different so I want to still keep it in my Object as it not equal. And then how to remove the duplicates? Thank you for your help
Update: The order of the list should not get affected by removing the duplicates. And the
listWithDuplicates
holds a SampleClass Object.
You can solve this problem by using four-args version of the Collector toMap()
, which expects the following arguments:
In case if you can't change the implementation of the equals/hashCode
in the ValueObject
you can introduce an auxiliary type that would serve as a Key.
public record FirstNameLastName(String firstName, String lastName) {
public FirstNameLastName(ValueObject value) {
this(value.getFirstName(), value.getLastName);
}
}
Note: if you're OK with overriding the equals/hashCode
contract of the ValueObject
on it's firstName
and lastName
then you don't the auxiliary type shown above. In the code below you can use Function.identity()
as both keyMapper and valueMapper of toMap()
.
And the stream can be implemented like this:
SampleClass listWithDuplicates = // initializing your domain object
List<ValueObject> uniqueValues = listWithDuplicates.getValues().stream()
.collect(Collectors.toMap(
FirstNameLastName::new, // keyMapper - creating Keys
Function.identity(), // valueMapper - generating Values
(left, right) -> right // mergeFunction - resolving duplicates
LinkedHashMap::new // mapFuctory - LinkedHashMap is needed to preserve the encounter order of the elements
))
.values().stream()
.toList();