I have a list of simple scala case class instances and I want to print them in predictable, lexicographical order using list.sorted
, but receive "No implicit Ordering defined for ...".
Is there exist an implicit that provides lexicographical ordering for case classes?
Is there simple idiomatic way to mix-in lexicographical ordering into case class?
scala> case class A(tag:String, load:Int)
scala> val l = List(A("words",50),A("article",2),A("lines",7))
scala> l.sorted.foreach(println)
<console>:11: error: No implicit Ordering defined for A.
l.sorted.foreach(println)
^
I am not happy with a 'hack':
scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)
This has the benefit that it is updated automatically whenever A changes. But, A's fields need to be placed in the order by which the ordering will use them.
The
unapply
method of the companion object provides a conversion from your case class to anOption[Tuple]
, where theTuple
is the tuple corresponding to the first argument list of the case class. In other words:To summarize, there are three ways to do this:
Define a custom ordering. The benefit of this solution is that you can reuse orderings and have multiple ways to sort instances of the same class:
Answering your question Is there any standard function included into the Scala that can do magic like List((2,1),(1,2)).sorted
There is a set of predefined orderings, e.g. for String, tuples up to 9 arity and so on.
No such thing exists for case classes, since it is not easy thing to roll off, given that field names are not known a-priori (at least without macros magic) and you can't access case class fields in a way other than by name/using product iterator.
The sortBy method would be one typical way of doing this, eg (sort on
tag
field):Since you used a case class you could extend with Ordered like such:
My personal favorite method is to make use of the provided implicit ordering for Tuples, as it is clear, concise, and correct:
This works because the companion to
Ordered
defines an implicit conversion fromOrdering[T]
toOrdered[T]
which is in scope for any class implementingOrdered
. The existence of implicitOrdering
s forTuple
s enables a conversion fromTupleN[...]
toOrdered[TupleN[...]]
provided an implicitOrdering[TN]
exists for all elementsT1, ..., TN
of the tuple, which should always be the case because it makes no sense to sort on a data type with noOrdering
.The implicit ordering for Tuples is your go-to for any sorting scenario involving a composite sort key:
As this answer has proven popular I would like to expand on it, noting that a solution resembling the following could under some circumstances be considered enterprise-grade™:
Given
es: SeqLike[Employee]
,es.sorted()
will sort by name, andes.sorted(Employee.orderingById)
will sort by id. This has a few benefits:Ordering
, so providing an ordering directly eliminates an implicit conversion in most cases.