From: "baweaver (Brandon Weaver)" Date: 2022-01-05T01:05:32+00:00 Subject: [ruby-core:106967] [Ruby master Feature#18181] Introduce Enumerable#min_with_value, max_with_value, and minmax_with_value Issue #18181 has been updated by baweaver (Brandon Weaver). I like the more generic suggestion from @ko1 on `with_value`, I had a similar idea before reading it when @shan pinged me about this ticket. The other potential is using the `_map` suffix as is the case with `filter_map`: ```ruby %w(abcde fg hijk).min_map { |e| e.size } # => ['fg', 2] %w(abcde fg hijk).max_map { |e| e.size } # => ['abcde', 5] %w(abcde fg hijk).minmax_map { |e| e.size } # => [['fg', 2], ['abcde', 5]] %w(abcde fg hijk).min_map(2) { |e| e.size } # => [['fg', 2], ['hijk', 4]] %w(abcde fg hijk).max_map(2) { |e| e.size } # => [['abcde', 5], ['hijk', 4]] ``` This has precedence, though I do wonder how many functions we accumulate in Enumerable that are effectively the composition of two or more Enumerable methods. More musing, and in a general sense, I'm reminded of Elixir extracting Enumerable-like methods into `Enum` and allowing potential composition and the rejected prior syntax of `Enumerable.:method` which ties into a [fairly old post](https://dev.to/baweaver/ruby-2-7-the-pipeline-operator-1b2d) on my hopes for the also rejected pipeline operator that was proposed ---------------------------------------- Feature #18181: Introduce Enumerable#min_with_value, max_with_value, and minmax_with_value https://bugs.ruby-lang.org/issues/18181#change-95795 * Author: kyanagi (Kouhei Yanagita) * Status: Open * Priority: Normal ---------------------------------------- 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: ``` ruby %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`. ``` ruby %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.) ``` ruby 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. ``` ruby 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. ``` ruby 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 ``` -- https://bugs.ruby-lang.org/ Unsubscribe: