如何使用未知数量的项目对Scala中的列表进行排序?

时间:2015-09-08 09:23:29

标签: scala sorting functional-programming scala-collections

我已经看到了这个问题How to sort a list in Scala by two fields?

这类似但不重复。

我可以使用前一个问题的答案轻松地对List[DataPoint]进行排序:

case class DataPoint(keys: List[String], value: Double)

listOfDataPoints.sortBy(point => (point.keys(0), point.keys(1)))

但我不知道keys中的项目数。我所知道的是,给定列表中的每个DataPoint都具有相同数量的键,因此绝不会对List("a")List("a", "b")进行排序。

那么如何用未知数量的键对列表进行排序呢?

2 个答案:

答案 0 :(得分:3)

你想做的是

datapoints.sortby(_.keys)

这显然不起作用。当我们看一下sortby的签名时,很明显为什么它不起作用:

sortBy[B](f: (A) ⇒ B)(implicit ord: math.Ordering[B]): List[A]

您的BList[String],而您没有Ordering[List[String]]的实例。那么我们该怎么办?我们提供一个!

我们需要做的是实施方法

def compare(x: T, y: T): Int

我们想比较以下内容:

  • 如果两个项目的第一个键不同,则使用该键进行排序
  • 否则,按列表的其余部分排序
  • 如果其中一个列表为空,则另一个列表首先出现 [1]

我们的T在这里是字符串,但我们对T的所有需求都是可比较的,所以我们可以更一般。

def listOrdering[T](implicit ord: Ordering[T]): Ordering[List[T]] = new Ordering[List[T]] {
  def compare(x: List[T], y: List[T]): Int = {
    (x, y) match {
      case (Nil, Nil) => 0 //both empty => equal
      case (Nil, _)   => -1 //one of the two empty => empty is the smallest
      case (_, Nil)   => 1 //one of the two empty => empty is the smallest
      case (xhead :: xtail, yhead :: ytail) => {
        val headdiff = ord.compare(xhead, yhead)
        if (headdiff == 0) compare(xtail, ytail) //recursively compare the tails if equivalent
        else (headdiff ) //otherwise, the difference in the heads
      }  
    }
  }
}

现在我们可以明确地为sortby方法提供排序:

datapoints.sortby(_.keys)(listOrdering)

或以隐含范围提供它们

[1]:你表示这种情况从未发生过,所以任何选择都足够好

答案 1 :(得分:1)

您可以定义自己的Ordering[List[String]]。例如,您可以定义:

class ListOrdering[A](implicit aOrd: math.Ordering[A]) extends Ordering[List[A]] {    
  def compare(a1: List[A], a2: List[A]) = (a1, a2) match {
    case (Nil, _) => if (a2.isEmpty) 0 else -1
    case (_, Nil) => 1
    case (h1 :: t1, h2 :: t2) => if (aOrd.compare(h1, h2) == 0) compare(t1, t2) else aOrd.compare(h1, h2)
  }    
}

然后在范围内的某处提供以下内容:

implicit val listOrd = new ListOrdering[String]

你可以写:

dps.sortBy(_.keys)

它应该有用。

请注意,我对ListOrdering的定义被推广为可用于范围内具有隐式A的任何类型Ordering[A],并且可以处理可变长度的列表(即使你这么说在您的情况下,您的密钥列表总是相同的长度。)