Reputation: 1272
I have very simple thing to do, i have list of persons like this:
[{
name: John,
date: 01-01-2018,
attend: true
},
{
name: Adam,
date: 01-01-2018,
attend: false
},
{
name: Adam,
date: 01-02-2018,
attend: true
},
{
name: JOHN,
date: 01-02-2018,
attend: false
}]
Result of this array should be: Adam (true), John (false)
So i need to return list of latest entries from users, in this case John first confirmed that he is attending, then he changed his mind and told that he is not attending so i'm returning his last entry (note that sometimes it's written JOHN and sometimes John but it is the same person, that is kind of tricky part)
My question what is best approach to filter out this kind of list, i was thinking applying "unique by property java stream" but first will need to order persons by date descending and names (to upper/lower case) and then i would need somehow to take latest entry.
Anyone have any good idea what is best approach to this ?
Upvotes: 7
Views: 1947
Reputation: 34460
A compact way without streams:
Map<String, User> map = new LinkedHashMap<>();
users.forEach(u -> map.merge(
u.getName().toLowerCase(),
u,
BinaryOperator.maxBy(Comparator.comparing(Person::getDate))));
Collection<User> result = map.values();
Or if you do need a List
:
List<User> result = new ArrayList<>(map.values());
This code uses Map.merge
, which puts the entry in the map if there's no entry with the same key (the lowercased user name), or, if the map already contains an entry for the key, it applies the merge function, which in this case chooses the User
instance with the max date
.
Upvotes: 1
Reputation: 1170
Despite all answers until now are functionally correct, please consider this option:
final Map<String, Boolean> lastAttendResults =
people
.stream()
.collect(
groupingBy(
Person::getName, // Define what is the property you are using to group people.
() -> new TreeMap<String, Boolean>(String.CASE_INSENSITIVE_ORDER), // Supply a map implementation that ignore name case.
collectingAndThen(maxBy(Comparator.comparing(Person::getDate)), // Foreach list of grouped people, select that with last date.
o -> o.get().isAttend()))); // As maxBy returns an Optional<Person> and we are sure that it exists, just get the Person and if he attends.
This implementation is interesting because it let be evident the concept of collection grouping. Although, in depth, it also uses a map, seems to me that use or not a map is not programmer problem, I mean, what we are looking for is how to group people by name and then get the last entry.
If you prefer receive a List of Person instead of a Map of name and attend, you can use:
final List<Person> lastAttendResults =
new ArrayList<>(people
.stream()
.collect(
groupingBy(Person::getName, // Define what is the property you are using to group people.
() -> new TreeMap<String, Person>(String.CASE_INSENSITIVE_ORDER), // Supply a map implementation that ignore name case.
collectingAndThen(maxBy(Comparator.comparing(Person::getDate)), // Foreach list of grouped people, select that with last date.
Optional::get // As maxBy returns an Optional<Person> and we are sure that is exists, just get the Person.
))).values());
Upvotes: 2
Reputation: 31978
You can use Collectors.toMap
to do the same as:
List<Person> finalList = new ArrayList<>(people.stream()
.collect(Collectors.toMap(a -> a.getName().toLowerCase(), // name in lowercase as the key of the map (uniqueness)
Function.identity(), // corresponding Person as value
(person, person2) -> person.getDate().isAfter(person2.getDate()) ? person : person2)) // merge in case of same name based on which date is after the other
.values()); // fetch the values
Note: The above assumes the minimal Person
class to be
class Person {
String name;
java.time.LocalDate date;
boolean attend;
// getters and setters
}
Upvotes: 8
Reputation: 56453
You could use the toMap
collector:
Collection<Person> values = source.stream()
.collect(toMap(e -> e.getName().toLowerCase(),
Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Person::getDate))))
.values();
see this answer for an explanation as to how toMap works
Upvotes: 5