Project

General

Profile

Actions

Feature #21386

open

Introduce `Enumerable#join_map`

Added by matheusrich (Matheus Richard) 3 days ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:122345]

Description

Problem

The pattern .map { ... }.join(sep) is extremely common in Ruby codebases:

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:

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.

users.join_map(", ", &:name)

A Ruby implementation could look like this:

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:

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:

[1, 2, 3].join(", ") { |i| -i } # => "-1, -2, -3"

Kotlin

Kotlin has a similar function called joinToString that can take a transformation function:

val chars = charArrayOf('a', 'b', 'c')
println(chars.joinToString() { it.uppercaseChar().toString() }) // A, B, C 

No data to display

Actions

Also available in: Atom PDF

Like1