【问题标题】:How to implement Ruby max_by to return all elements which have the maximum value?如何实现 Ruby max_by 以返回所有具有最大值的元素?
【发布时间】:2021-03-26 20:13:45
【问题描述】:

Ruby max_by 方法从数组中找到最大元素。有时最大元素具有多重性,在这种情况下 max_by 只选择其中一个,看似随意。当我需要所有这些时,我目前使用这种方法,在数组数组中找到最大值:

sorted=ary.sort_by{|a,b| b}.reverse
max_score=sorted.first[1]
t=sorted.take_while{|z| z[1]==max_score}

但是我怎么能用“maxes_by”方法对 Array 类进行猴子修补,该方法接受一个块,类似于 max_by,并返回一个最大值数组?

【问题讨论】:

  • 没有内置的,所以你必须自己编写。如果需要,您可以将其引入为Array#maxes_by,甚至可以将其放入Enumerable
  • 你的输入数组是什么样子的?
  • 我想要一个适用于各种数组、嵌套数组等的实现。这就是为什么应该传递一个块。无论如何,我的数组包含值对,作为嵌套数组,这也可以从我的示例中看出。

标签: arrays ruby sorting monkeypatching maxima


【解决方案1】:

无需编写返回预期输出的新优化方法,您可以简单地将max_byselect 组合起来:

maximum = array.max_by { |element| element[1] }
t = array.select { |element| element[1] == maximum[1] }

另一种选择可能是按相关值(group_by)对所有元素进行分组,然后选择具有最大值的列表。

lists = array.group_by { |element| element[1] }
lists[lists.keys.maximum]

【讨论】:

    【解决方案2】:

    值得一提的是,该任务可以在一次遍历数组中完成,或者更一般地说,一次遍历任何类包含Enumerable 的集合。

    module Enumerable
      def max_by_all
        return each unless block_given?
        last_yield = nil
        each_with_object([]) do |e,a|
          ye = yield(e)
          case last_yield.nil? ? -1 : last_yield <=> ye
          when -1
            a.replace([e])
            last_yield = ye
          when 0
            a << e
          end
        end
      end
    end
    
    arr = [2, 4, 3, 4, 1, 2, 5, 3, 5, 1]
    arr.max_by_all(&:itself)
      #=> [5, 5]
    
    arr =  ["style", "assets", "misty", "assist", "corgi", "bossy", "bosses", "chess"]
    arr.max_by_all { |s| s.count('s') }
      #=> ["assets", "assist", "bosses"] 
    
    h = { a: 1, b: 3, c: 2, d: 3, e: 1 }
    h.max_by_all(&:last)
      #=> [[:b, 3], [:d, 3]] 
    
    arr = [1, 2, 3]
    arr.max_by_all.map { |n| 2*n }
      #=> [2, 4, 6] 
    

    在最后一个示例中,max_by_all 没有块,因此返回一个仅枚举 self 元素的枚举器。这种行为可能看起来毫无意义,但我已经提供了它(return each unless block_given? 行)来模仿Enumerable#max_by 在没有提供块时的行为。

    【讨论】:

    • 我怎样才能用这个猴子修补 Array 类?
    • @Konstantin 您可以在这里查看解决方案 Monkey Patching the Array 类:stackoverflow.com/a/65336503/7644846。但是您可以使用 ruby​​ 改进来进行猴子补丁,而不会损坏您的系统。
    • 康斯坦丁,我修改了我的答案来解决你的问题。
    【解决方案3】:

    使用猴子补丁

    class Array
      def maxes_by
        maximal = max_by { |x| yield(x) }
        select { |x| yield(x) == yield(maximal) }
      end
    end
    
    • 用法
    > ['house', 'car', 'mouse'].maxes_by { |x| x.length }
    
    => ['house', 'mouse']
    

    但我不建议对 Array 类进行猴子补丁,这种做法很危险,可能会对您的系统造成不良影响。

    为了我们的利益,ruby 语言提供了一个很好的功能来解决这个问题,Refinements,这是猴子修补 ruby​​ 的安全方法。

    为简化起见,您可以使用 RefinementsArray 类进行猴子修补,并且更改将仅在使用细化的类的范围内可用! :)

    您可以在您正在学习的课程中使用细化,然后就可以开始了。

    使用优化

    module MaxesByRefinement
      refine Array do
        def maxes_by
          maximal = max_by { |x| yield(x) }
          select { |x| yield(x) == yield(maximal) }
        end
      end
    end
    
    class MyClass
      using MaxesByRefinement
    
      def test
        a = %w(house car mouse)
        a.maxes_by { |x| x.length } # maxes_by is available here!
      end
    end
    
    • 用法
    > MyClass.new.test
    
    => ['house', 'mouse']
    

    【讨论】:

      猜你喜欢
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 1970-01-01
      相关资源
      最近更新 更多