在Ruby中基于多个条件对数组进行排序

25

我有一个多维数组,如下所示:

[
  [name, age, date, gender]
  [name, age, date, gender]
  [..]
]

我想知道根据多个条件对这个数组进行排序的最佳方法...例如,如何首先根据年龄排序,然后再按名称排序?

我试着使用sort方法操作它,像这样:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] }

除此之外,我并不真正理解这个语法,我得到的结果也不如我所期望的那样。 我应该使用sort方法吗?我应该通过mapping数组逐个比较结果吗?


1
@pruett:没有不尊重robbrit的回答,但你应该考虑选择的答案,使用Enumerable#sort没有任何问题,除非 Enumerable#sort_by可以胜任。这可能会让来到这里的人产生误解。 - tokland
可能是“Ruby按多个值排序?”的重复问题。 - jtbandes
3个回答

50

你应该始终使用sort_by进行按键排序。不仅可读性更强,而且效率也更高。此外,为了可读性,我还喜欢使用解构赋值绑定:

array.sort_by {|name, age| [age, name] }

16

这应该可以解决问题:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] }

那么这段代码是做什么的呢?它使用了很多 Ruby 的惯用语。

  • 首先是块,类似于其他语言中的回调或匿名函数/类。Array 的 sort 方法使用它们来基于块的返回值比较两个元素。你可以在这里阅读有关它们的全部内容。
  • 接下来是 <=> 运算符。如果第一个参数小于第二个参数,则返回 -1;如果相等,则返回 0;如果第一个大于第二个,则返回 1。当你将其与数组一起使用时,它会逐个比较数组元素,直到其中一个返回 -1 或 1。如果数组相等,则返回 0。

10
你也可以使用 sort_by { |a| [a[1], a[0]] },构造所有这些数组并非免费。 - mu is too short

7
我理解您想首先按年龄排序,然后如果有多个记录年龄相同,则按名称排列该子集。
这对我来说是有效的。
people = [
      ["bob", 15, "male"], 
      ["alice", 25, "female"], 
      ["bob", 56, "male"], 
      ["dave", 45, "male"], 
      ["alice", 56, "female"], 
      ["adam", 15, "male"]
    ]

people.sort{|a,b| (a[1] <=> b[1]) == 0 ? (a[0] <=> b[0]) : (a[1] <=> b[1]) }

# The sorted array is

[["adam", 15, "male"], 
 ["bob", 15, "male"], 
 ["alice", 25, "female"], 
 ["dave", 45, "male"], 
 ["alice", 56, "female"], 
 ["bob", 56, "male"]]

这段代码的作用是先按照年龄进行比较,如果年龄相同(<=>返回0),则再按照姓名进行比较。

非常好!我喜欢那个语法。 - pruett
如果年龄相同,我们想按姓名降序排序怎么办? - inquisitive
@Inquisitive 那么在这种情况下,我认为您应该在右侧的“(a[1] <=> b[1])”中交换“a”和“b”。 - user1515295

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接