Search code examples
javasortinggwtjava-8comparator

How can I sort a list of Java objects based on parameters that a user selects from a drop down menu?


I understand how to build and use Comparator for lists of objects based on multiple attributes of those objects. However, I'm not sure how to do that efficiently by giving the user the choice of which parameters to sort by. This is a GWT UI.

For example, I have two drop-down menus with 11 parameters each, corresponding to the attributes of the objects. Each menu also as a button beside to specify ascending/descending order.

So, the user may choose of example, to sort by fileName, desc and timestamp, asc. How can I sort these objects, without a ridiculously long if statement for the few hundred possible combinations?

I could do this if the sort was fixed:

files.sort(Comparator.comparing(FileInfo::getFileName).thenComparing(FileInfo::getTimestamp).reversed());

(I might have got asc/desc backwards there)
But I shouldn't have to type that out for every conceivable option. So how should I do that?


Solution

  • You can try creating an enum:

    public enum FileInfoComparator {
        FILE_NAME(Comparator.comparing(FileInfo::getFileName)),
        TIMESTAMP(Comparator.comparing(FileInfo::getTimestamp)),
        SIZE(Comparator.comparing(FileInfo::getSize)),
        // ... add other criteria here
        ;
    
        private final Comparator<FileInfo> comparator;
    
        FileInfoComparator(Comparator<FileInfo> comparator) {
            this.comparator = comparator;
        }
    
        static Comparator<FileInfo> by(String key, boolean ascending) {
            Comparator<FileInfo> comparator = valueOf(key).comparator;
            return ascending ? comparator : comparator.reversed();
        }
    
        static Comparator<FileInfo> by(Pair<String, Boolean> criterion) {
            return by(criterion.getKey(), criterion.getValue());
        }
    
        static Optional<Comparator<FileInfo>> by(List<Pair<String, Boolean>> criteria) {
            return criteria.stream()
                    .map(FileInfoComparator::by)
                    .filter(Objects::nonNull)
                    .reduce(Comparator::thenComparing);
        }
    
    }
    

    Then use:

    // Create the list of selected criteria based on user input:
    // E.g. [FILE_NAME, false] will mean "sort by fileName descending"
    List<Pair<String, Boolean>> selectedCriteria = ...
    
    // Build your comparator and sort your list
    FileInfoComparator.by(selectedCriteria).ifPresent(files::sort);
    

    In case of empty user selection the files will remain untouched.