根据Scala中的多个约束对列表进行排序

Way*_*kie 1 sorting scala scala-collections

我拼命想要找到一种方法来对字符串列表进行排序,其中字符串是以下形式的预定义标识符:a1.1,a1.2,...,a1.100,a2.1,a2.2,. ...,a2.100,...,b1.1,b1.2,..等等,这是正确的排序.因此,每个标识符首先按其第一个字符(降序字母顺序)排序,并在此排序中按连续数字排序.我已经尝试了sortWith,它提供了一个排序函数,为所有两个连续的列表成员指定上述规则.

scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => a.take(1) < b.take(1) && a.drop(1).toDouble < b.drop(1).toDouble)
res2: List[java.lang.String] = List(a1.102, a1.1, b2.2, b2.1)
Run Code Online (Sandbox Code Playgroud)

这不是我预期的顺序.但是,通过交换表达式的顺序,如

scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => (a.drop(1).toDouble < b.drop(1).toDouble && a.take(1) < b.take(2)))
res3: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2)
Run Code Online (Sandbox Code Playgroud)

这确实给了我(至少在这个例子中)所需的顺序,我也不理解.

我会非常感激,如果有人能给我一个暗示那里到底发生了什么以及如何按照我的意愿对列表进行排序(使用比仅比较<或>更复杂的布尔表达式).另一个问题:我正在排序的字符串(在我的示例中)实际上是来自HashMap m的键.任何解决方案都会通过其中的键对其进行排序

m.toSeq.sortWith((a: (String, String), b: (String, String)) => a._1.drop(1).toDouble < b._1.drop(1).toDouble && a._1.take(1) < b._1.take(1))
Run Code Online (Sandbox Code Playgroud)

非常感谢先进!

Tra*_*own 6

更新:我误读了你的例子 - 你想要a1.2先于a1.102,toDouble下面的版本将无法正确.我建议以下内容:

items.sortBy { s =>
  val Array(x, y) = s.tail.split('.')
  (s.head, x.toInt, y.toInt)
}
Run Code Online (Sandbox Code Playgroud)

这里我们使用Scala的Ordering实例Tuple3[Char, Int, Int].


看起来你的第二个("正确的")版本中有一个拼写错误:b.take(2)应该没有意义,应该b.take(1)匹配第一个.一旦你解决了这个问题,就会得到相同(不正确)的订单.

真正的问题是,在数字匹配的情况下,您只需要第二个条件.所以以下工作按需要:

val items = List("a1.102", "b2.2", "b2.1", "a1.1")
items.sortWith((a, b) =>
  a.head < b.head || (a.head == b.head && a.tail.toDouble < b.tail.toDouble)
)
Run Code Online (Sandbox Code Playgroud)

不过我实际上建议如下:

items.sortBy(s => s.head -> s.tail.toDouble)
Run Code Online (Sandbox Code Playgroud)

在这里,我们利用Scala提供适当Ordering实例的事实Tuple2[Char, Double],因此我们可以提供一个转换函数,将您的项目转换为该类型.

并回答你的上一个问题:是的,这些方法中的任何一个都应该适用于你的Map例子.