Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi criteria sorting of a list of objects with Guava Ordering

I have a class WHICH CANNOT implement comparable, but needs to be sorted based on 2 fields. How can I achieve this with Guava?

Let's say the class is:

class X {
  String stringValue;
  java.util.Date dateValue;
} 

And I have a list of these:

List<X> lotsOfX;

I want to sort them based on the value field first and then based on dateValue descending within each 'group' of 'value' fields.

What I have been doing so far is:

List<X> sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(dateValueSortFunction).reverse().sortedCopy(lotsOfX));
sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(stringValueSortFunction).sortedCopy(sortedList));

The functions are defined as:

public class DateValueSortFunction<X> implements Function<X, Long> {

    @Override
      public Long apply(X input) {
        return input.getDateValue().getTime();  //returns millis time
      }
}

And:

public class StringValueSortFunction<X> implements Function<X, Integer> {

      @Override
        public Integer apply(X input) {
          if(input.getStringValue().equalsIgnoreCase("Something"))
            return 0;
          else if(input.getStringValue().equalsIgnoreCase("Something else"))
            return 1;
          else
            return 2;
        }
}

Expected output in sortedList is:

Something   03/18/2013
Something   03/17/2013
Something else  03/20/2013
Something else  03/19/2013
....

My approach works but is obviously inefficient for traversing the list twice. Is there a better way of doing this?

I am using this in a GWT app. Implementing comparable is not an option.

like image 735
user949110 Avatar asked Mar 20 '13 19:03

user949110


2 Answers

I suspect you want Ordering.compound. You could do it all in one statement, but I'd use:

Ordering<X> primary = Ordering.natural().onResultOf(stringValueSortFunction);
Ordering<X> secondary = Ordering.natural()
                              .onResultOf(dateValueSortFunction)
                              .reverse();
Ordering<X> compound = primary.compound(secondary);

List<X> sortedList = compound.immutableSortedCopy(lotsOfX);
like image 134
Jon Skeet Avatar answered Sep 27 '22 17:09

Jon Skeet


Java 8 provides methods on Comparator to concisely specify chained comparators. Together with the newly-introduced List.sort, you can do:

lotsOfX.sort(
    Comparator.comparingInt(x -> stringValueSortFunction.apply(x.stringValue))
        .thenComparing(x -> x.dateValue, Comparator.reverseOrder()));

This mutates the list, of course -- make a copy first if you want to leave the original list unchanged, or wrap the comparator in an Ordering and use immutableSortedCopy if you want an immutable copy.

like image 37
Jeffrey Bosboom Avatar answered Sep 27 '22 18:09

Jeffrey Bosboom