如何实现Ruby max_by以返回所有具有最大值的元素?

w8ntj3qf  于 2023-05-06  发布在  Ruby
关注(0)|答案(3)|浏览(161)

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,并返回一个最大值的数组?

h6my8fg2

h6my8fg21#

无需编写新的优化方法来返回预期的输出,您可以合并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]
avkwfej4

avkwfej42#

值得一提的是,该任务可以通过数组的单次传递来完成,或者更一般地,通过类包含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的行为。

p1iqtdky

p1iqtdky3#

使用Monkey-Patch

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上进行猴子修补的安全方法。
为了简化,使用Refinements,您可以对Array类进行Monkey修补,并且更改将仅在使用改进的类的范围内可用!:)
您可以在正在处理的类中使用细化,并且可以开始了。

使用精化

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

相关问题