我拼命想要找到一种方法来对字符串列表进行排序,其中字符串是以下形式的预定义标识符: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)
这不是我预期的顺序。但是,通过交换表达式的顺序,如
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)
这确实给了我(至少在这个例子中)所需的顺序,我也不理解。
我会非常感激,如果有人可以给我一个暗示,那里到底发生了什么以及我如何按照自己的意愿对列表进行排序(使用比仅仅比较&lt;或&gt;更复杂的布尔表达式)。另一个问题:我正在排序的字符串(在我的示例中)实际上是来自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))
非常感谢先进!
答案 0 :(得分:6)
更新:我误读了您的示例 - 您希望a1.2
位于a1.102
之前,toDouble
以下版本无法正确使用items.sortBy { s =>
val Array(x, y) = s.tail.split('.')
(s.head, x.toInt, y.toInt)
}
。我建议如下:
Ordering
我们在这里使用Scala的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)
)
我实际上建议如下:
items.sortBy(s => s.head -> s.tail.toDouble)
在这里,我们利用了Scala为Ordering
提供适当的Tuple2[Char, Double]
实例的事实,因此我们可以提供一个转换函数,将您的项目转换为该类型。
回答你的上一个问题:是的,这些方法中的任何一种都应该适用于你的Map
示例。
答案 1 :(得分:0)
因此,请考虑当您的排序函数通过a="a1.1"
和b="a1.102"
时会发生什么。
你想要的是函数返回true。但是,a.take(1) < b.take(1)
返回false,因此函数返回false。
更仔细地考虑你的案例
所以试试这个:
(a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1)
然后返回正确的顺序:
scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1))
res8: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2)
逆转顺序对你有用的原因是运气。考虑额外的输入"c0"
,看看发生了什么:
scala> List("c0", "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)))
res1: List[java.lang.String] = List(c0, a1.1, a1.102, b2.1, b2.2)
反转函数首先对字符串的数字部分进行排序,然后对前缀进行排序。只是碰巧你给出的数字顺序也保留了前缀排序,但情况并非总是如此。
答案 2 :(得分:0)
创建一个元组,其中包含"."
之前的字符串,然后是"."
之后的整数。这将使用第一部分的词典顺序和第二部分的整数顺序。
scala> val order = Ordering.by((s:String) => (s.split("\\.")(0),s.split("\\.")(1).toInt))
order: scala.math.Ordering[String] = scala.math.Ordering$$anon$7@384eb259
scala> res2
res8: List[java.lang.String] = List(a1.5, a2.2, b1.11, b1.8, a1.10)
scala> res2.sorted(order)
res7: List[java.lang.String] = List(a1.5, a1.10, a2.2, b1.8, b1.11)