在Ruby中基于值和键对哈希进行排序

10

如何在Ruby中按值和键对哈希进行排序?例如

h = {4 => 5, 2 => 5, 7 => 1}

会按照顺序排列

[[7, 1], [2,5], [4, 5]]

我可以通过执行以下操作来按值排序

h.sort {|x,y| x[1] <=> y[1]}

但是如果值相同,我无法想出如何基于值和键进行排序。

1个回答

20
h.sort_by {|k, v| [v, k] }

这个方法利用了Array混入Comparable并逐个元素地定义了<=>

需要注意的是,上面的代码等效于:

h.sort_by {|el| el.reverse }

等价于

h.sort_by(&:reverse)

可能更易读,也可能不是。

如果你知道哈希表通常是先按键排序,然后按值排序,那么上面的内容就很明显了。也许可以写一个简短的注释:

h.sort_by(&:reverse) # sort by value first, then by key
注意:如果你只是想委托给某个属性的 `<=>` 方法(即按照键排序而不是一般比较函数),通常最好使用 `sort_by` 而不是 `sort`。它更容易阅读。一般来说,它也更快,但可读性方面更为重要。
仅在绝对必要时才编写自己的比较函数。
因此,你非工作示例应该改写为:
h.sort_by {|el| el[1] }

个人而言,我更喜欢在块参数列表中使用解构绑定,而不是使用 el[0]el[1]

h.sort_by {|key, value| value }

然而,在这种特殊情况下,el[1]也恰好与el.last相同, 因此您可以简单地写成:

h.sort_by(&:last)

在不是绝对必要的情况下,使用“键控”排序而不是定义比较函数会更好。 - Karl Knechtel
1
哈希在Enumerable中混合,使用集合的each方法实现sortsort_by。哈希的each方法生成[k,v]数组,块中的|k,v|因此相当于k,v=[kx,vx]并行赋值。正如Jörg的|el|示例所示,您可以在那里使用单个变量并获得行为el=[kx,vx]。至于这件事,您可以做|a,b,c,d,e|并获得a,b,c,d,e=[kx,vx],尽管显然这不会很有用。 - glenn mcdonald
1
@Karl Knechtel:可能更容易演示:def foo; yield 1; yield 1, 2; yield 1, 2, 3 end; puts '1 param'; foo {|a| p a }; puts '2 params'; foo {|a, b| p a, b }; puts '3 params'; foo {|a, b, c| p a, b, c }。简而言之:块参数语义比方法参数语义宽松得多。它们的行为更像多重赋值。(事实上,在 Ruby 1.9 之前,块确实使用赋值,这意味着您可以做一些非常疯狂的事情,比如 foo {|@bar, baz.quux|},它会将传递到块中的值分配给实例变量 @bar 并调用 setter baz.quux=。) - Jörg W Mittag
对于第一个例子,h.sort_by {|k, v| [v, k] } 等同于 h.sort_by {|k, v| v },更清晰地显示您正在使用值进行排序。 - Jeff Tsui
如何按值升序和键降序排序? - inquisitive
显示剩余6条评论

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