ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map`
@ 2025-05-30 16:33 matheusrich (Matheus Richard) via ruby-core
  2025-07-05  6:42 ` [ruby-core:122653] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2025-05-30 16:33 UTC (permalink / raw)
  To: ruby-core; +Cc: matheusrich (Matheus Richard)

Issue #21386 has been reported by matheusrich (Matheus Richard).

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122653] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
@ 2025-07-05  6:42 ` prateekkish@gmail.com (Prateek Choudhary) via ruby-core
  2025-07-05  7:50 ` [ruby-core:122654] " nobu (Nobuyoshi Nakada) via ruby-core
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: prateekkish@gmail.com (Prateek Choudhary) via ruby-core @ 2025-07-05  6:42 UTC (permalink / raw)
  To: ruby-core; +Cc: prateekkish@gmail.com (Prateek Choudhary)

Issue #21386 has been updated by prateekkish@gmail.com (Prateek Choudhary).


PR: https://github.com/ruby/ruby/pull/13792

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113933

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122654] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
  2025-07-05  6:42 ` [ruby-core:122653] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
@ 2025-07-05  7:50 ` nobu (Nobuyoshi Nakada) via ruby-core
  2025-07-05  7:57 ` [ruby-core:122655] " nobu (Nobuyoshi Nakada) via ruby-core
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: nobu (Nobuyoshi Nakada) via ruby-core @ 2025-07-05  7:50 UTC (permalink / raw)
  To: ruby-core; +Cc: nobu (Nobuyoshi Nakada)

Issue #21386 has been updated by nobu (Nobuyoshi Nakada).


prateekkish@gmail.com (Prateek Choudhary) wrote in #note-2:
> PR: https://github.com/ruby/ruby/pull/13792

This difference is intentional?

```ruby
[1,2,3].map {|n|[n]}.join(",") #=> "1,2,3"
[1,2,3].join_map(",") {|n|[n]} #=> "[1],[2],[3]"
```

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113934

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122655] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
  2025-07-05  6:42 ` [ruby-core:122653] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
  2025-07-05  7:50 ` [ruby-core:122654] " nobu (Nobuyoshi Nakada) via ruby-core
@ 2025-07-05  7:57 ` nobu (Nobuyoshi Nakada) via ruby-core
  2025-07-05 14:27 ` [ruby-core:122656] " matheusrich (Matheus Richard) via ruby-core
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: nobu (Nobuyoshi Nakada) via ruby-core @ 2025-07-05  7:57 UTC (permalink / raw)
  To: ruby-core; +Cc: nobu (Nobuyoshi Nakada)

Issue #21386 has been updated by nobu (Nobuyoshi Nakada).


This code would show the difference more clearly.

```ruby
[[1,2],3].map {|n|[n]}.join("|") #=> "1|2|3"
[[1,2],3].join_map("|") {|n|[n]} #=> "[[1, 2]]|[3]"
```

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113935

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122656] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (2 preceding siblings ...)
  2025-07-05  7:57 ` [ruby-core:122655] " nobu (Nobuyoshi Nakada) via ruby-core
@ 2025-07-05 14:27 ` matheusrich (Matheus Richard) via ruby-core
  2025-07-05 17:39 ` [ruby-core:122657] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2025-07-05 14:27 UTC (permalink / raw)
  To: ruby-core; +Cc: matheusrich (Matheus Richard)

Issue #21386 has been updated by matheusrich (Matheus Richard).


My expectation is that join_map would behave like map + join

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113936

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122657] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (3 preceding siblings ...)
  2025-07-05 14:27 ` [ruby-core:122656] " matheusrich (Matheus Richard) via ruby-core
@ 2025-07-05 17:39 ` prateekkish@gmail.com (Prateek Choudhary) via ruby-core
  2025-07-09 18:41 ` [ruby-core:122699] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: prateekkish@gmail.com (Prateek Choudhary) via ruby-core @ 2025-07-05 17:39 UTC (permalink / raw)
  To: ruby-core; +Cc: prateekkish@gmail.com (Prateek Choudhary)

Issue #21386 has been updated by prateekkish@gmail.com (Prateek Choudhary).


nobu (Nobuyoshi Nakada) wrote in #note-4:
> This code would show the difference more clearly.
> 
> ```ruby
> [[1,2],3].map {|n|[n]}.join("|") #=> "1|2|3"
> [[1,2],3].join_map("|") {|n|[n]} #=> "[[1, 2]]|[3]"
> ```

Hmm. I missed considering that behavior. In this example though, just the `map` would return wrapped arrays:
```ruby
[[1,2],3].map {|n|[n]} #=> [[[1, 2]], [3]]
```
So somehow doing a map and join is doing more of a flat_map + join. 
I can correct the PR to mimic that behavior. 

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113937

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122699] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (4 preceding siblings ...)
  2025-07-05 17:39 ` [ruby-core:122657] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
@ 2025-07-09 18:41 ` Dan0042 (Daniel DeLorme) via ruby-core
  2025-07-10  2:54 ` [ruby-core:122701] " mame (Yusuke Endoh) via ruby-core
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2025-07-09 18:41 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #21386 has been updated by Dan0042 (Daniel DeLorme).


I am against this.

ergonomics: Adding a special "X_Y" method for every common pattern of "X followed by Y" is truly horrible for the ergonomics of the language. It only multiplies the number of useless details the programmer should remember, without adding any expressiveness. Just because "map + join" is a common pattern doesn't mean it benefits from being expressed as a single method.

performance: This appears intended to improve performance, but is there any benchmark? Is this really a measurable cost in any program, anywhere? It seems to me like a good JIT with escape analysis might already handle this for you, without having to remember the "special method for performance". Sorry but it looks entirely like premature micro-optimization to me.

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113976

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122701] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (5 preceding siblings ...)
  2025-07-09 18:41 ` [ruby-core:122699] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2025-07-10  2:54 ` mame (Yusuke Endoh) via ruby-core
  2025-07-10  8:38 ` [ruby-core:122709] " matz (Yukihiro Matsumoto) via ruby-core
  2025-07-10  8:51 ` [ruby-core:122712] " knu (Akinori MUSHA) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-07-10  2:54 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #21386 has been updated by mame (Yusuke Endoh).


https://github.com/ruby/dev-meeting-log/blob/master/2019/DevMeeting-2019-03-11.md

> [Feature #15323] Proposal: Add Enumerable#filter_map
> ...
> mame: if this method is accepted, other methods with map will be also requested.
> matz: filter_map is not simply combination of filter and map.  so mame’s concern is needless fears.
> matz: ok, accepted `filter_map`.

That's why I was against it 🤦

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113978

* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
### 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122709] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (6 preceding siblings ...)
  2025-07-10  2:54 ` [ruby-core:122701] " mame (Yusuke Endoh) via ruby-core
@ 2025-07-10  8:38 ` matz (Yukihiro Matsumoto) via ruby-core
  2025-07-10  8:51 ` [ruby-core:122712] " knu (Akinori MUSHA) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: matz (Yukihiro Matsumoto) via ruby-core @ 2025-07-10  8:38 UTC (permalink / raw)
  To: ruby-core; +Cc: matz (Yukihiro Matsumoto)

Issue #21386 has been updated by matz (Yukihiro Matsumoto).

Status changed from Open to Rejected

I reject this proposal. Simply combine join and map at the moment.
I hope JIT inlining will remove intermediate objects in the future.

Matz.


----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113986

* 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122712] [Ruby Feature#21386] Introduce `Enumerable#join_map`
  2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
                   ` (7 preceding siblings ...)
  2025-07-10  8:38 ` [ruby-core:122709] " matz (Yukihiro Matsumoto) via ruby-core
@ 2025-07-10  8:51 ` knu (Akinori MUSHA) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: knu (Akinori MUSHA) via ruby-core @ 2025-07-10  8:51 UTC (permalink / raw)
  To: ruby-core; +Cc: knu (Akinori MUSHA)

Issue #21386 has been updated by knu (Akinori MUSHA).


FWIW, this function is called `mapconcat` in Emacs: https://www.gnu.org/software/emacs/manual/html_node/elisp/Mapping-Functions.html#index-mapconcat

I once wanted this when I implemented shelljoin() and chose to just call map and join.  I convinced myself that you shouldn't worry too much about performance.
https://github.com/ruby/ruby/blob/65a0f46880ecb13994d3011b7a95ecbc5c61c5a0/lib/shellwords.rb#L209

----------------------------------------
Feature #21386: Introduce `Enumerable#join_map`
https://bugs.ruby-lang.org/issues/21386#change-113989

* 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/

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-07-10  8:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-30 16:33 [ruby-core:122345] [Ruby Feature#21386] Introduce `Enumerable#join_map` matheusrich (Matheus Richard) via ruby-core
2025-07-05  6:42 ` [ruby-core:122653] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
2025-07-05  7:50 ` [ruby-core:122654] " nobu (Nobuyoshi Nakada) via ruby-core
2025-07-05  7:57 ` [ruby-core:122655] " nobu (Nobuyoshi Nakada) via ruby-core
2025-07-05 14:27 ` [ruby-core:122656] " matheusrich (Matheus Richard) via ruby-core
2025-07-05 17:39 ` [ruby-core:122657] " prateekkish@gmail.com (Prateek Choudhary) via ruby-core
2025-07-09 18:41 ` [ruby-core:122699] " Dan0042 (Daniel DeLorme) via ruby-core
2025-07-10  2:54 ` [ruby-core:122701] " mame (Yusuke Endoh) via ruby-core
2025-07-10  8:38 ` [ruby-core:122709] " matz (Yukihiro Matsumoto) via ruby-core
2025-07-10  8:51 ` [ruby-core:122712] " knu (Akinori MUSHA) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).