【问题标题】:Ruby sort by boolean and numberRuby 按布尔值和数字排序
【发布时间】:2023-03-11 07:55:01
【问题描述】:

我使用的是 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 - 错误

【问题讨论】:

    标签: ruby arrays sorting boolean


    【解决方案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 中的排序不稳定

        您需要做的就是让您的排序块不返回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
        

        (无需明确将布尔值与truefalse 进行比较)

        【讨论】:

          【解决方案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(联合)合并两个集合,而“+”连接两个集合(结果不是真正的集合,而是一个普通数组)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-10-28
            • 2021-10-13
            • 2014-09-06
            • 2021-10-28
            • 1970-01-01
            • 1970-01-01
            • 2014-05-13
            相关资源
            最近更新 更多