I was trying to filter a list based on multiple conditions, sorting.
class Student{
private int Age;
private String className;
privat
Use the toMap collector:
Collection values = students.stream()
.collect(toMap(Student::getName,
Function.identity(),
BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
.values();
Explanation
We're using this overload of toMap
:
toMap(Function super T,? extends K> keyMapper,
Function super T,? extends U> valueMapper,
BinaryOperator mergeFunction)
Student::getName
above is the keyMapper
function used to extract the values for the map keys.Function.identity()
above is the valueMapper
function used to extract the values for the map values where Function.identity()
simply returns the elements in the source them selves i.e. the Student
objects.BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))
above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student
.values()
returns us a collection of students.The equivalent C# code being:
var values = students.GroupBy(s => s.Name, v => v,
(a, b) => b.OrderByDescending(e => e.Age).Take(1))
.SelectMany(x => x);
Explanation (for those unfamiliar with .NET)
We're using this extension method of GroupBy:
System.Collections.Generic.IEnumerable GroupBy
(this System.Collections.Generic.IEnumerable source,
Func keySelector,
Func elementSelector,
Func,TResult> resultSelector);
s => s.Name
above is the keySelector
function used to extract the value to group by.v => v
above is the elementSelector
function used to extract the values i.e. the Student
objects them selves.b.OrderByDescending(e => e.Age).Take(1)
above is the resultSelector
which given an IEnumerable
represented as b
takes the oldest student..SelectMany(x => x);
to collapse the resulting IEnumerable>
into a IEnumerable
.