Ruby 按布尔值和数字排序
Posted
技术标签:
【中文标题】Ruby 按布尔值和数字排序【英文标题】:Ruby sort by boolean and number 【发布时间】:2011-05-04 17:19:13 【问题描述】:我使用的是 Ruby 1.8.7。我有以下哈希数组。我需要先按布尔值排序,但这些结果也必须按原始顺序排序。我基本上需要将所有真正的散列移到数组的顶部,但保持原来的顺序。
任何帮助将不胜感激!
array = [:id => 1, :accepts => false,
:id => 2, :accepts => false,
:id => 3, :accepts => true,
:id => 4, :accepts => false,
:id => 5, :accepts => true]
sorted = array.sort do |x, y|
if x[:accepts] == y[:accepts]
0
elsif x[:accepts] == true
-1
elsif x[:accepts] == false
1
end
end
我得到的这种类型:
5 - 真 3 - 真 2 - 错误 4 - 错误 1 - 错误
我需要它来让步:
3 - 真 5 - 真 1 - 错误 2 - 错误 4 - 错误
【问题讨论】:
【参考方案1】:array = [:id => 1, :accepts => false,
:id => 2, :accepts => false,
:id => 3, :accepts => true,
:id => 4, :accepts => false,
:id => 5, :accepts => true]
sorted = array.sort do |x, y|
if x[:accepts] ^ y[:accepts]
x[:accepts] ? -1 : 1
else
x[:id] <=> y[:id]
end
end
puts sorted
如果您愿意,也可以使用!=
而不是^
。
【讨论】:
【参考方案2】:这样就可以了:
array.sort|a,b| (a[:accepts] == b[:accepts]) ? ((a[:id] < b[:id]) ? -1 : 1) : (a[:accepts] ? -1 : 1)
【讨论】:
宾果游戏!感谢一百万菲利普! 我不喜欢这个解决方案,因为它不保留作者提到的原始订单作为要求。相反,它暗示:id
已被订购并误用它作为辅助键。
也许这是一个要求集,因为他习惯了另一种语言。 Ruby Array 类不知道可以更改元素位置的操作。恕我直言,在使用 Ruby 时坚持内置函数很重要,因为它可以节省性能。 (尤其是 MRI。)但是代码并不意味着预购,用 irb 试试吧。
@hurikhan77 哦,我明白你的意思了。尽管如此,它还是相当简单的代码。
@Philip 在了解 OP 可能不想排序但真正拆分和合并之后,我有一个想法解决这个问题而不进行排序。 :id
似乎是一个辅助键,仅此而已。在下面查看我的答案(想知道为什么它在没有给出理由的情况下被否决)。【参考方案3】:
如果:accepts
相等,您可以在:id
键上添加额外检查,如下所示:
array = [:id => 1, :accepts => false,
:id => 2, :accepts => false,
:id => 3, :accepts => true,
:id => 4, :accepts => false,
:id => 5, :accepts => true]
sorted = array.sort do |x, y|
if x[:accepts] == y[:accepts]
if x[:id] == y[:id]
0
elsif x[:id] > y[:id]
1
elsif x[:id] < y[:id]
-1
end
elsif x[:accepts] == true
-1
elsif x[:accepts] == false
1
end
end
【讨论】:
【参考方案4】:a.sort_by |x| (x[:accepts] ? 0 : 99999) + x[:id]
更新:嗯,显然这需要x[:id].respond_to? "+"
,另外还有相对于常量的范围限制。
然而,这是最短的,可能也是最快的答案,如果也显然是最有问题的。
真正重要的一课是它说明了人们应该超越Array
(或其他)并检查Enumerable
是否在(your object).class.ancestors
中。这些问题及其观众通常在回答“接下来我应该了解 Ruby 什么,我怀疑还有其他方法”。
不管这是否是一种很好的排序方式(诚然这是有问题的),这个答案建议 #sort_by
并且只是 查找文档 为 #sort_by
(它不在数组中)将教一个小但对初学者来说很重要。
【讨论】:
这是一种非常糟糕和愚蠢的答案,看起来很聪明。不适合初学者在学习新语言的第一步寻求帮助 - 特别是在没有任何解释的情况下。 这个被否决的答案与@glenn mcdonald 的被投赞成票的答案有什么不同?两个答案都可以解释一下,这是真的,格伦的要好一点,但这个也没有那么不好。 “非常糟糕和愚蠢”不是一个非常协作的评论,但客观地说,我认为这个评论有三个可识别的缺陷,使其成为一个不太理想的建议。最明显的是,它假设 id 不会高于 99999。现在或以后很容易出错。其次,像 99999 这样的魔法值通常是清晰度和理解力的敌人,因此也是可维护性的敌人。第三,这个版本使用了加法,这在功能上是不相关的,如果 id 后来变成字符串,或者负数或其他东西,它就会中断。 如果这种方法具有补偿性优点,或者替代方案具有复杂的约束条件,则可以容忍任何这些事情。但是做一个包含两个元素的数组而不是添加两个数字就更清楚了。那有意义吗? (为了清楚起见,我没有对此投反对票,我只是想解释一下。) @glenn 实际上我写了“有点”,但点了。 +1...由于添加了解释,投票发生了变化【参考方案5】:对这些事情使用sort_by
,而不是sort
!
array.sort_by |h| [h[:accepts] ? 0 : 1,h[:id]]
【讨论】:
@Nakilon,在我的机器上并使用 MRI 1.8.7,格伦的代码花费的时间不到你代码的一半。 我并不是说sort
没有比sort_by
快的情况,但我自己从未遇到过。通常sort_by
明显更快。更重要的是,编写一个产生一元排序值的函数总是比一堆二进制比较案例更清晰、更清晰。就个人而言,我保留sort
用于根据比较的配对特征实际需要执行不同逻辑的情况。就像如果我需要比较字符串,如果它们都是数字,或者如果它们都是日期,或者按字符串比较......【参考方案6】:
这是因为 Ruby 1.8.7 中的排序不稳定
http://redmine.ruby-lang.org/issues/show/1089 http://en.wikipedia.org/wiki/Stable_sort#Stability您需要做的就是让您的排序块不返回0
:
sorted = array.sort do |x, y|
if x[:accepts] == y[:accepts]
x[:id] <=> y[:id] # not 0
elsif x[:accepts]
-1
else
1
end
end
(无需明确将布尔值与true
和false
进行比较)
【讨论】:
【参考方案7】:好吧,从您的问题中,我推断您确实想按 :accepts
值对结果进行分组,并将两个结果集合并回一个数组。我对此的解决方案是:
array.select |where| where[:accepts] | array.reject |where| where[:accepts]
# => [:accepts=>true, :id=>3,
# :accepts=>true, :id=>5,
# :accepts=>false, :id=>1,
# :accepts=>false, :id=>2,
# :accepts=>false, :id=>4]
这将保持原始顺序,而不会在 :id
键上暗示任何排序。这意味着您不需要辅助键来保留顺序,并且无论传输的数据如何,您都可以保留结果的顺序。
这也可能有用(也许正是您进一步评估所需要的):
array.group_by |where| where[:accepts]
# => false=>[:accepts=>false, :id=>1,
# :accepts=>false, :id=>2,
# :accepts=>false, :id=>4],
# true=>[:accepts=>true, :id=>3,
# :accepts=>true, :id=>5]
同样,不涉及人工排序...group_by
是 1.8.7 中的新功能。
PS:如果您不希望第一个代码 sn-p 从数组中删除重复项,请将 bar 运算符替换为 plus 运算符。 “|”根据theory of sets(联合)合并两个集合,而“+”连接两个集合(结果不是真正的集合,而是一个普通数组)。
【讨论】:
以上是关于Ruby 按布尔值和数字排序的主要内容,如果未能解决你的问题,请参考以下文章