Reputation: 3331
I have a comma separated string which holds the sort criteria for sorting a list of Students:
Input:
String sortOrder = "height, weight, age"
List<student> students;
Each of the elements above are Comparators on the student object that sort the Student object with some complex logic. I need to know how best to translate the sort order as it appears in the sortOrder string and activate the matching comparators in that order. So for the above example, the height comparator would run first and the age one last.
Upvotes: 0
Views: 138
Reputation: 137329
If I understand correctly what you want, you want to sort the list of students by their heights, then their weight and then their age. But you want this list of property to be dynamic.
This means we need to implement a custom Comparator
that works with a given property of a given class.
A first implementation could be creating a Map
where each String property is mapped to an associate Comparator
. This would be a clean approach (see @Manos Nikolaidis' answer, and Misha's comment, for this implementation).
A true dynamic solution is possible using a little bit of reflection: first the declared field having the given name of the given class is retrieved. It is set accessible since this field is most likely private. Finally, a Comparator
comparing the value of this field for each student is returned. This code assumes blindly that the target property is in fact Comparable
(otherwise, why would we want to compare using this field?).
private static <U> Comparator<U> comparingProperty(String property, Class<U> clazz) {
try {
Field field = clazz.getDeclaredField(property);
field.setAccessible(true);
return Comparator.comparing(s -> {
try {
@SuppressWarnings("unchecked")
Comparable<Object> comparable = (Comparable<Object>) field.get(s);
return comparable;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
});
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
}
Once that we have this utility comparator, we can sort a list of student easily. A stream of the sort properties is created by splitting around ","
and trimming the results. Then, each property is mapped to its corresponding Comparator
and, finally, this stream is reduced by combining all of the comparators together with Comparator::thenComparing
:
students.sort(Stream.of(sortOrder.split(","))
.map(String::trim)
.map(s -> comparingProperty(s, Student.class))
.reduce(Comparator::thenComparing)
.get()
);
Upvotes: 1
Reputation: 22264
You can make a HashMap
mapping these words to Comparator
objects. Then use the Comparators for sorting
Map<String, Comparator<student>> comparators = new HashMap<>();
after you add Comparator
objects to comparators
like this :
comparators.put("height", Comparator.comparingDouble(student::getHeight));
If you would like to perform different sorts consecutively, just go through the words in the sortOrder
and apply e.g.
for (String comp : sortOrder.split(", "))
Collections.sort(students, comparators.get(comp));
Upvotes: 2