Well, everything went happily (though with ugly syntax) with Scala´s List.sortBy and unary minus operators... until I needed to perform mixed ascending/descending sorts with strings. Not so easy to implement elegantly.
I tried to google answers but didn't find any useful results. That´s why I decided to make my own syntax by using generics, Ordered trait inheritance and Scala´s implicit Ordering support. I ended up making the following implementation:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object OrderingDSL { | |
case class AscendingOrder[T](value: T)(implicit ord: Ordering[T]) extends Ordered[AscendingOrder[T]] { | |
def compare(that: AscendingOrder[T]): Int = ord.compare(value, that.value) | |
} | |
case class DescendingOrder[T](value: T)(implicit ord: Ordering[T]) extends Ordered[DescendingOrder[T]] { | |
def compare(that: DescendingOrder[T]): Int = -ord.compare(value, that.value) | |
} | |
def asc[T](value: T)(implicit ord: Ordering[T]) = AscendingOrder(value) | |
def desc[T](value: T)(implicit ord: Ordering[T]) = DescendingOrder(value) | |
} |
myList.sortBy(v => (asc(v.mem1), desc(v.mem2), ...))
Syntax should work with all classes having Scala Ordering trait implemented (at least all built-in basic types in Scala). Here is a working example how to use the DSL:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object OrderingDSLExample extends App { | |
case class My(num: Int, str: String) | |
val myValues = List(My(3, "foo"), My(3, "bar"), My(5, "bar"), My(5, "foo")) | |
import OrderingDSL._ | |
require(myValues.sortBy(m => (asc(m.num), asc(m.str))) == | |
List(My(3, "bar"), My(3, "foo"), My(5, "bar"), My(5, "foo"))) | |
require(myValues.sortBy(m => (asc(m.num), desc(m.str))) == | |
List(My(3, "foo"), My(3, "bar"), My(5, "foo"), My(5, "bar"))) | |
require(myValues.sortBy(m => (desc(m.num), asc(m.str))) == | |
List(My(5, "bar"), My(5, "foo"), My(3, "bar"), My(3, "foo"))) | |
require(myValues.sortBy(m => (desc(m.num), desc(m.str))) == | |
List(My(5, "foo"), My(5, "bar"), My(3, "foo"), My(3, "bar"))) | |
println("Horray!") | |
} |