如何实现 Ruby max_by 到 return 所有具有最大值的元素?

How to implement Ruby max_by to return all elements which have the maximum value?

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”方法猴子修补数组class,它接受一个块,类似于max_by,returns一个最大值数组?

无需编写 returns 预期输出的新优化方法,您只需组合 max_by and select:

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]

值得一提的是,该任务可以通过数组单次传递完成,或者更一般地说,通过任何 class 包含 Enumerable.[=20 的集合单次传递即可完成=]

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 没有块,因此 returns 是一个仅枚举 self 元素的枚举器。这种行为可能看起来毫无意义,但我已经提供了它(return each unless block_given? 行)以模仿 Enumerable#max_by 在没有提供块时的行为。

使用猴子补丁

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 class 进行猴子修补,这种做法很危险,可能会对您的系统造成不良影响。

为了我们好,ruby 语言提供了一个很好的特性来克服这个问题,Refinements,这是在 ruby.[=18= 上进行猴子修补的安全方法]

为简化起见,使用 Refinements 你可以猴子修补 Array class 并且更改将仅在正在使用的 class 范围内可用细化! :)

您可以在您正在处理的 class 中使用优化,您可以开始了。

使用优化

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']