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



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";方法,它接受一个块,类似于maxby,并返回一个最大值数组?

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

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

使用Monkey补丁

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类进行猴子补丁,并且更改将仅在使用该优化的类的范围内可用!:(

您可以在正在处理的类中使用精化,然后就可以开始了。

使用优化

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

最新更新