如何按两个字段对Scala中的列表进行排序?


101

如何在Scala中按两个字段对列表进行排序,在此示例中,我将按lastName和firstName进行排序?

case class Row(var firstName: String, var lastName: String, var city: String)

var rows = List(new Row("Oscar", "Wilde", "London"),
                new Row("Otto",  "Swift", "Berlin"),
                new Row("Carl",  "Swift", "Paris"),
                new Row("Hans",  "Swift", "Dublin"),
                new Row("Hugo",  "Swift", "Sligo"))

rows.sortBy(_.lastName)

我尝试这样的事情

rows.sortBy(_.lastName + _.firstName)

但这不起作用。因此,我对一个好的简单解决方案感到好奇。

Answers:


216
rows.sortBy(r => (r.lastName, r.firstName))

4
如果我们想对lastName进行倒序排序,然后对firstName进行自然排序怎么办?
萨钦K

14
@SachinK:您必须OrderingRow类创建自己的类,并将其与sorted类似这样的方法一起使用:rows.sorted(customOrdering)。您也可以使用custom OrderingTuple2例如:rows.sortBy(r => (r.lastName, r.firstName))( Ordering.Tuple2(Ordering.String.reverse, Ordering.String) )
senia 2014年

5
@SachinK:您可以实现customOrderingOrdering[Row]手动或使用Ordering.by这样的:val customOrdering = Ordering.by((R:行)=>(r.lastName,r.firstName))(Ordering.Tuple2(Ordering.String.reverse,Ordering.String)) `
senia

1
优秀的。或按降序排列rows.sortBy(r => (-r.field1, -r.field2))
布伦特·浮士德

@BrentFaust您不能-与一起使用String。您应该使用Ordering::reverse这种方式:rows.sortBy(r => (r.lastName, r.firstName))(implicitly[Ordering[(String, String)]].reverse)
senia

12
rows.sortBy (row => row.lastName + row.firstName)

如果要按合并的名称排序(如您的问题所示),或者

rows.sortBy (row => (row.lastName, row.firstName))

如果您首先要按lastName排序,则firstName; 与较长的名称(Wild,Wilder,Wilderman)相关。

如果你写

rows.sortBy(_.lastName + _.firstName)

带有2个下划线的方法需要两个参数:

<console>:14: error: wrong number of parameters; expected = 1
       rows.sortBy (_.lastName + _.firstName)
                               ^

1
其顺序可能与按名字,然后按姓氏排序不同。
Marcin 2012年

1
具体来说,当姓氏长度不同时
Luigi Plinge'4

7

通常,如果您使用稳定的排序算法,则可以仅按一个键进行排序,然后按另一个键进行排序。

rows.sortBy(_.firstName).sortBy(_.lastName)

最终结果将按姓氏排序,然后在相等的情况下按名字排序。


您确定Scala sortBy使用稳定排序吗?否则,这个答案是没有意义的。
om-nom-nom 2012年

1
@ om-nom-nom:scala-lang.org/api/current/scala/util/Sorting$.html quickSort仅为值类型定义,所以可以。
Marcin 2012年

1
rows是一个不可变的列表,它sortBy返回一个新值,而不是对它起作用的值进行突变(即使在可变类中)。因此,您的第二个表达式只是对原始未排序列表进行排序。
路易吉·普林格

3
Scala在sortBy方法的幕后使用java.util.Arrays.sort,用于对象数组的保证稳定。因此,是的,此解决方案是正确的。(已在Scala 2.10中进行了检查)
Marcin Pieciukiewicz

1
考虑这种性能与创建元组的单个sortBy的性能比较有趣。通过这种方法,您显然不必创建这些元组,但是通过元组方法,您只需要比较姓氏匹配的名字。但是我想这没关系-如果您要编写对性能至关重要的代码,则根本不应该使用sortBy!
AmigoNico 2014年

-3

也许这仅适用于元组列表,但是

scala> var zz = List((1, 0.1), (2, 0.5), (3, 0.6), (4, 0.3), (5, 0.1))
zz: List[(Int, Double)] = List((1,0.1), (2,0.5), (3,0.6), (4,0.3), (5,0.1))

scala> zz.sortBy( x => (-x._2, x._1))
res54: List[(Int, Double)] = List((3,0.6), (2,0.5), (4,0.3), (1,0.1), (5,0.1))

似乎有效,并且是表达它的简单方法。


但是不适用于字符串,这是OP排序的内容。
原型保罗

这个问题已经有几个很好的答案,不仅限于元组列表。那么发布它的原因是什么?
2014年

@honk:在元组列表上,先前的解决方案实际上不起作用(AFAICT)。如果我不是Scala的新手,也许我会理解如何修改那些先前的解决方案以在这种情况下工作,但是今天我不知道。我认为我的答案可能会帮助另一位Scala新手完成我尝试做的事情。
spreinhardt 2014年

@ user3508605:非常感谢您的贡献。但是,堆栈溢出的想法是针对特定问题(如此处的情况)提出问题,并回答能够解决这些特定问题(且仅针对那些问题)的问题。您的答案为另一个问题提供了解决方案。因此,这是张贴在错误的地方。如果您认为答案很有价值,请提出一个新问题。在新问题中描述您的相应问题,然后在此处发布答案。最后,不要忘了在这里删除您的答案。谢谢您的合作!
2014年

@honk:当然,我将答案转移到另一个问题上。而且,如果我可以要求您在以前对这个问题的回答(来自Marcin)上添加评论,那似乎是错误的。(我没有足够的可信度点可以张贴在上面。)该答案中的示例仅按一个键进行排序,然后按另一个键进行排序,从而有效地消除了第一种结果。至少在一个元组列表上。
spreinhardt 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.