How do I sort a collection of Lists in lexicographic order in Scala?

前端 未结 6 1746
清酒与你
清酒与你 2020-12-09 18:38

If A has the Ordered[A] trait, I\'d like to be able to have code that works like this

val collection: List[List[A]] = ... // constr         


        
6条回答
  •  长情又很酷
    2020-12-09 19:25

    Inspired by Ben Lings' answer, I wrote my own version of sort:

    def sort[A : Ordering](coll: Seq[Iterable[A]]) = coll.sorted
    

    which is equivalent to:

    def sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sorted
    

    Note that ordering is implicitly converted to Ordering[Iterable[A]].

    Examples:

    scala> def sort[A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A]) = coll.sorted
    sort: [A](coll: Seq[Iterable[A]])(implicit ordering: Ordering[A])Seq[Iterable[A]]
    
    scala> val coll = List(List(1, 3), List(1, 2), List(0), Nil, List(2))
    coll: List[List[Int]] = List(List(1, 3), List(1, 2), List(0), List(), List(2))
    
    scala> sort(coll)
    res1: Seq[Iterable[Int]] = List(List(), List(0), List(1, 2), List(1, 3), List(2))
    

    It was asked how to supply your own comparison function (say, _ > _ instead of _ < _). It suffices to use Ordering.fromLessThan:

    scala> sort(coll)(Ordering.fromLessThan(_ > _))
    res4: Seq[Iterable[Int]] = List(List(), List(2), List(1, 3), List(1, 2), List(0))
    

    Ordering.by allows you to map your value into another type for which there is already an Ordering instance. Given that also tuples are ordered, this can be useful for lexicographical comparison of case classes.

    To make an example, let's define a wrapper of an Int, apply Ordering.by(_.v), where _.v extracts the underlying value, and show that we obtain the same result:

    scala> case class Wrap(v: Int)
    defined class Wrap
    
    scala> val coll2 = coll.map(_.map(Wrap(_)))
    coll2: List[List[Wrap]] = List(List(Wrap(1), Wrap(3)), List(Wrap(1), Wrap(2)), List(Wrap(0)), List(), List(Wrap(2)))
    
    scala> sort(coll2)(Ordering.by(_.v))
    res6: Seq[Iterable[Wrap]] = List(List(), List(Wrap(0)), List(Wrap(1), Wrap(2)), List(Wrap(1), Wrap(3)), List(Wrap(2)))
    

    Finally, let's do the same thing on a case class with more members, reusing the comparators for Tuples:

    scala> case class MyPair(a: Int, b: Int)
    defined class MyPair
    
    scala> val coll3 = coll.map(_.map(MyPair(_, 0)))
    coll3: List[List[MyPair]] = List(List(MyPair(1,0), MyPair(3,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(0,0)), List(), List(MyPair(2,0)))
    
    scala> sort(coll3)(Ordering.by(x => (x.a, x.b)))
    res7: Seq[Iterable[MyPair]] = List(List(), List(MyPair(0,0)), List(MyPair(1,0), MyPair(2,0)), List(MyPair(1,0), MyPair(3,0)), List(MyPair(2,0)))
    

    EDIT:

    My definition of sort above is deprecated in 2.13:

    warning: method Iterable in object Ordering is deprecated (since 2.13.0):
    Iterables are not guaranteed to have a consistent order; if using a type
    with a consistent order (e.g. Seq), use its Ordering (found in the
    Ordering.Implicits object)
    

    Use instead:

    def sort[A](coll: Seq[Seq[A]])(implicit ordering: Ordering[A]) = {
      import Ordering.Implicits._
      coll.sorted
    }
    

提交回复
热议问题