From: "zverok (Victor Shepelev) via ruby-core" Date: 2025-07-16T05:50:08+00:00 Subject: [ruby-core:122794] [Ruby Feature#21386] Introduce `Enumerable#join_map` Issue #21386 has been updated by zverok (Victor Shepelev). Just a thought: shouldn't we add `#join` to `Enumerator::Lazy`? It wouldn't solve "logical repetitiveness" of the pattern, but might be a good _and_ idiomatic way to optimize the pattern when necessary. (Lazy enumerators are underused in the community, as I understand. And I personally would like to see them more popular.) ---------------------------------------- Feature #21386: Introduce `Enumerable#join_map` https://bugs.ruby-lang.org/issues/21386#change-114075 * Author: matheusrich (Matheus Richard) * Status: Rejected ---------------------------------------- ### Problem The pattern `.map { ... }.join(sep)` is extremely common in Ruby codebases: ```ruby users.map(&:name).join(", ") ``` It���s expressive but repetitive (both logically and computationally). This pattern allocates an intermediate array and does two passes over the collection. Real-world usage is widespread: - [Open source Ruby projects using this pattern](https://github.com/search?q=lang%3Aruby+%2F%5C.map%5Cs*%5C%7B%5B%5E%7D%5D*%5C%7D%5C.join%2F&type=code) - [Within Rails](https://github.com/search?q=lang%3Aruby+%2F%5C.map%5Cs*%5C%7B%5B%5E%7D%5D*%5C%7D%5C.join%2F+org%3Arails&type=code) - [Within Ruby itself](https://github.com/search?q=lang%3Aruby+%2F%5C.map%5Cs*%5C%7B%5B%5E%7D%5D*%5C%7D%5C.join%2F+org%3Aruby&type=code) ### Proposal Just like `filter_map` exists to collapse a common `map + compact`, this proposal introduces `Enumerable#join_map`, which maps and joins in a single pass. ```ruby users.join_map(", ", &:name) ``` A Ruby implementation could look like this: ```ruby module Enumerable def join_map(sep = "") return "" unless block_given? str = +"" first = true each do |item| str << sep unless first str << yield(item).to_s first = false end str end end ``` The name `join_map` follows the precedent of `filter_map`, emphasizing the final operation (`join`) over the intermediate (`map`). ### Prior Art Some other languages have similar functionality, but with different names or implementations: #### Elixir Elixir has this via [the `Enum.map_join/3` function](https://hexdocs.pm/elixir/1.12/Enum.html#map_join/3): ```elixir Enum.map_join([1, 2, 3], &(&1 * 2)) "246" Enum.map_join([1, 2, 3], " = ", &(&1 * 2)) "2 = 4 = 6" ``` #### Crystal Crystal, on the other hand, [uses `Enumerable#join` with a block](https://crystal-lang.org/api/1.16.3/Enumerable.html#join%28separator%3D%22%22%2C%26%3AT-%3E%29-instance-method): ```crystal [1, 2, 3].join(", ") { |i| -i } # => "-1, -2, -3" ``` #### Kotlin Kotlin has a similar [function called `joinToString`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/join-to-string.html) that can take a transformation function: ```kotlin val chars = charArrayOf('a', 'b', 'c') println(chars.joinToString() { it.uppercaseChar().toString() }) // A, B, C ``` -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/