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.)
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.
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