Search code examples
javajava-stream

How to dynamically choose a comparator to sort a collection of data by?


So I have user input that's a list of 2 elements, and the order of the 2 elements might change. If the input is like ["byName", "byAge"], then I'd need to first sort a collection of data based off name, and if there's a tie between 2 elements, then sort by age.

So if input is

["byName", "byAge"] 

and data is

[{"John", 81}, 
 {"Bob", 81}, 
 {"David", 47}] 

sorted output would be

[{"Bob", 81}, 
 {"David", 47}, 
 {"John", 81}]

Likewise, if the input list is reversed like ["byAge", "byName"], then I'd sort the data by age, and use the name as a tiebreaker.

I'm unsure of what's the cleanest way to dynamically choose a comparator (I need comparators because there's other custom comparison logic I need to implement) based off the input? The most obvious way would be to directly perform a check on which element exists at index 0, i.e. in some lambda chain if input.get(0) == "byAge" then sort like this. But that's not really extensible, is it?


Solution

  • You can create a map of comparators by name:

    Map<String, Comparator<Person>> comparators = new HashMap<>();
    comparators.put("byName", Comparator.comparing(Person::getName));
    comparators.put("byAge", Comparator.comparing(Person::getAge));
    

    Then chain them on the fly:

    List<String> input = Arrays.asList("byName", "byAge");
    Comparator<Person> comparator = input.stream()
            .map(comparators::get)
            .reduce(Comparator::thenComparing)
            .orElse((a, b) -> 0);