Feature #18181
openIntroduce Enumerable#min_with_value, max_with_value, and minmax_with_value
Description
PR is https://github.com/ruby/ruby/pull/4874
I propose Enumerable#min_with_value
, max_with_value
and minmax_with_value
.
These methods work like this:
%w(abcde fg hijk).min_with_value { |e| e.size } # => ['fg', 2]
%w(abcde fg hijk).max_with_value { |e| e.size } # => ['abcde', 5]
%w(abcde fg hijk).minmax_with_value { |e| e.size } # => [['fg', 2], ['abcde', 5]]
Corresponding to #min(n)
, an integer argument can be passed to #min_with_value
or #max_with_value
.
%w(abcde fg hijk).min_with_value(2) { |e| e.size } # => [['fg', 2], ['hijk', 4]]
%w(abcde fg hijk).max_with_value(2) { |e| e.size } # => [['abcde', 5], ['hijk', 4]]
Motivation¶
When I use Enumerable#min_by
, I sometimes want to get not only the minimum element
but also the value from the given block.
(e.g.: There are many points. Find the nearest point and get distance to it.)
elem = enum.min_by { |e| foo(e) }
value = foo(elem)
This works, but I'd like to avoid writing foo() twice. (Consider a more complex case.)
This can be written without repeated foo() like belows, but it is slightly complicated and needs extra arrays.
value, elem = enum.map { |e| [foo(e), e] }.min_by(&:first)
If the size of enum is enormous, it is hard to use intermediate arrays.
Enumerable#min_with_value
solves this problem.
I think min_with_value
is the best name I could think of, but any suggestions for better names would be appreciated.
Benchmark¶
https://bugs.ruby-lang.org/issues/18181#note-2
Example¶
Solving a traveling salesman problem in nearest neighbor algorithm.
require 'set'
Point = Struct.new(:x, :y)
points = Set.new([Point.new(1, 1), Point.new(2, 4), Point.new(3, 3), Point.new(2, 2), Point.new(0, 1)])
total = 0
current = points.first
points.delete(current)
path = [current]
until points.empty?
current, distance = points.min_with_value do |point|
Math.hypot(current.x - point.x, current.y - point.y)
end
total += distance
points.delete(current)
path << current
end
p path
p total