Reputation: 553
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.
Upvotes: 13
Views: 10226
Reputation: 1502106
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);
Upvotes: 24
Reputation: 13663
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.
Upvotes: 2
Reputation: 198211
A less functional, but arguably cleaner, solution:
new Ordering<X>() {
public int compare(X x1, X x2) {
return ComparisonChain.start()
.compare(x1.stringValue, x2.stringValue)
.compare(x2.dateValue, x1.dateValue) // flipped for reverse order
.result();
}
}.immutableSortedCopy(listOfXs);
Upvotes: 17