2

我定义了一个

case class User(var firstName: String, var lastName: String, var city: String, var price: Int)

并且想要排序

val users = List(
  User("Peter", "Fox", "Berlin", 30),
  User("Otto",  "Schmidt", "Berlin", 20),
  User("Carl",  "Schmidt", "Berlin", 30),
  User("Igor",  "Schmidt", "Berlin", 10),
  User("Hugo",  "Schmidt", "Berlin", 50))

我可以做到这一点,例如

val sorted = users.sortBy(p => (p.lastName,p.firstName))

是否可以通过这样的序列给 sortBy 排序标准?

val sortCriteria = Seq(lastname,firstname)
val sorted = test.sortBy(p => sortCriteria)

我想按用户请求将序列中的标准与 1 到 n 个参数组合在一起,而不定义所有可能的组合。

4

4 回答 4

2

您需要的是一种比较两个Users 的方法。逻辑是:比较第一个条件,如果是1或-1,就是结果,否则如果是0,比较下一个条件。

我们有一点问题,因为只提供一个Seq[User => Any],我们无法说服编译器Any将具有定义的顺序。那么如何定义一个类来记住如何比较实例而不必记住它正在比较的字段的类型:

case class Criterion[T](crit: User => T)(implicit ord: Ordering[T]) {
  def compare(x: User, y: User): Int = ord.compare(crit(x), crit(y))
}

然后我们只需要实现顺序比较:

def ord(criteria: Seq[Criterion[_]]) = new Ordering[User] {
  def compare(x: User, y: User) = {
    def loop(crits: Seq[Criterion[_]]): Int = crits match {
      case Seq() => 0
      case c +: cs => c.compare(x, y) match {
        case 0 => loop(cs)
        case i => i
      }
    }
    loop(criteria)
  }
}

我们现在可以用你上面的数据试试这个

val cs = Seq(Criterion(_.price), Criterion(_.firstName))
val sorted = users.sorted(ord(cs))

/* results:
sorted: List[User] = List(
  User(Igor,Schmidt,Berlin,10),       Sorted by Price
  User(Otto,Schmidt,Berlin,20), 
  User(Carl,Schmidt,Berlin,30),      
  User(Peter,Fox,Berlin,30),      <-- Peter now after Carl
  User(Hugo,Schmidt,Berlin,50)) */
于 2013-08-14T20:46:06.367 回答
1

您需要使用 a Tuple,而不是 a Seq

  def sortCritera(user: User): Tuple2[String, String] = (user.firstName, user.lastName)
  println(users.sortBy(user => sortCritera(user)));

  def sortCritera2(user: User): Tuple3[String, String, String] = (user.city, user.firstName, user.lastName)
  println(users.sortBy(user => sortCritera(user)));
于 2013-08-14T15:57:34.177 回答
0

那么你可以做这样的事情。因为在内部它使用稳定排序(合并排序),因此以下解决方案有效:

val order = List("lastName","firstName")

def sort(ls:List[User],n:String) = n match {
  case "firstName" => ls.sortBy(p => p.firstName)
  case "lastName" => ls.sortBy(p => p.lastName)
  case "city" => ls.sortBy(p => p.city)
  case "price" => ls.sortBy(p => p.price)
}

order.reverse.foldLeft(users)((all,param) => sort(all,param))

基本上以相反的顺序,您firstName首先排序。然后按lastName.

这给出了相同的结果users.sortBy(p => (p.lastName,p.firstName))

于 2013-08-14T15:38:22.067 回答
0

接受的答案很好,但我们可以做得更多。我们可以将类作为类型动态传递,因此 Criterion 可以用于不同的类。排序中的排序方向很重要,因此也应该使用它。

升级后的 Criterion 类的示例代码应如下所示:

case class Criterion[A,T](crit: A => T, ascending:Boolean)(implicit ord: Ordering[T]) {
  def compare(x: A, y: A): Int = {
    val result = ord.compare(crit(x), crit(y))
    if(ascending) result else result * -1 //If descending change result to opposite value
  }
}

然后像这样为 Criterion 类创建伴随对象:

object Criterion {
  def ord[ A ]( criteria: Seq[ Criterion[ A, _ ] ] ): Ordering[ A ] = ( x: A, y: A ) => {
    def loop( crits: Seq[ Criterion[ A, _ ] ] ): Int = crits match {
      case Seq() => 0
      case c +: cs => c.compare( x, y ) match {
        case 0 => loop( cs )
        case i => i
      }
    }
    loop( criteria )
  }
}

排序如下所示:

val users = List(
  User("Peter", "Fox", "Berlin", 30),
  User("Otto",  "Schmidt", "Berlin", 20),
  User("Carl",  "Schmidt", "Berlin", 30),
  User("Igor",  "Schmidt", "Berlin", 10),
  User("Hugo",  "Schmidt", "Berlin", 50))

val crits = Seq(
  Criterion[ User, String ]( _.name, true ),
  Criterion[ User, Int]( _.price, false )
)

val sorted = users.sorted( Criterion.ord[ User ]( crits ) )
于 2019-08-16T21:28:40.143 回答