Using JB Nizet suggested solution, I have put together an entire working solution in which you can group by n number of fields.
- Grouping on any number of fields are possible
- Result is independent of grouping field order
- User can define aggregation stragery
This nested property will help us store the key for our grouping.
public class NestedProperty {
private final Field property;
private final Object value;
}
Field here is a simple object which will be feed at runtime. We can have better alternative to decide its type.
public class Field{
String name;
Class type;
}
This interface should be implementation by POGO to define what is the aggregation strategy.
public interface Aggregatable<T> {
public void add(T o);
}
Then using NestedProperty object we group the records till n-1 fields using streams.groupby function.
Map<List<NestedProperty>, List<T>> aggregatedRecords = objects.stream()
.collect(Collectors.groupingBy(r -> createGroupingKey(Nminus1fields, r), Collectors.toList()));
private static List<NestedProperty> createGroupingKey(java.util.List<Field> fields, Object r) {
return fields.stream().map(p -> p.toValue(r, p)).collect(Collectors.toList());
}
Then we can run the main aggregation method
List<?> result = objects.stream().filter( r -> r!=null )
.collect(Collectors.groupingBy(
record -> {
try {
return cast.cast(PropertyUtils.getNestedProperty(record, field.getName()));
}catch(Exception e) {
System.out.println("Property not found.");
}
return null;
}))
.entrySet().stream()
.map( e -> e.getValue().stream()
.reduce((f1, f2) -> {
try {
return (T) add(classDefination, f1, f2);
} catch (Exception e1) {
System.out.println("Error is method add()");
}
return null;
})
).map(f -> f.get())
.collect(Collectors.toList());
Please refer to answer in below link:
http://www.unbounded.in/group-by-n-fields-in-java-like-sql-using-streams-api/