# Additional Joined Functions

**URL:** <https://forums.swift.org/t/additional-joined-functions/22518>\
**Category:** Discussion\
**Created:** [April 2, 2019, 2:09pm UTC](https://forums.swift.org/t/additional-joined-functions/22518 "2019-04-02T14:09:15Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Wildchild9](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wildchild9/32/9397_2.png) [@Wildchild9](https://forums.swift.org/u/Wildchild9)\
**Post date:** [April 2, 2019, 2:09pm UTC](https://forums.swift.org/t/additional-joined-functions/22518/1 "2019-04-02T14:09:15Z")

</div>

# Additional `joined` Functions

I frequently find myself wanting to join collections whose elements do not conform to `StringProtocol`. I would like to suggest the addition of some new `joined` functions as extensions on `Collection`.

```
public extension Collection where Element: CustomStringConvertible {

    /// Returns a new string by concatenating the elements of the sequence, adding the given separator between each element.
    ///
    /// The following example shows how an array of integers can be joined to a single, dash-separated string:
    ///
    /// let phoneNumberSections = [492, 573, 5843]
    /// let phoneNumber = phoneNumberSections.joined(separator: ", ")
    /// print(phoneNumber)
    /// // Prints "492-573-5843"
    ///
    /// - Parameters:
    /// - separator: A string to insert between each of the elements in this collection. The default separator is an empty string.
    ///
    /// - Returns: A single, concatenated string.
    ///
    @inlinable func joined(separator: String = "") -> String {
        guard let first = first else { return "" }
        guard count > 1 else { return "\(first)" }
        return dropFirst().reduce(into: "\(first)") { $0.append(separator + "\($1)") }
    }
}

public extension Collection {
         
    /// Returns a new string by mapping the given closure over the collection's elements and concatenating `String` results, adding the given separator between each string.
    ///
    /// The following example shows how an array of integers can be transformed into each integer's respective square and joined to a single string separated by addition operators:
    ///
    /// let numbers = [1, 2, 3, 4, 5, 6, 7]
    /// let sumOfSquares = numbers.joined(separator: " + ") { "\($0 * $0)" }
    /// print(sumOfSquares)
    /// // Prints "1 + 4 + 9 + 16 + 25 + 36 + 49"
    ///
    /// - Parameters:
    /// - separator: A string to insert between each of the elements of this collection transformed into strings. The default separator is an empty string.
    /// - transform: A mapping closure that produces a string. `transform` accepts an element of this collection as its parameter and returns a transformed `String` value.
    ///
    /// - Returns: A single, concatenated string.
    ///
    @inlinable func joined(separator: String = "", _ transform: (Element) throws -> String) rethrows -> String {
        guard let first = first else { return "" }
        guard count > 1 else { return try transform(first) }
        return try dropFirst().reduce(into: try transform(first)) { $0.append(separator + (try transform($1))) }
    }
}

```

* * *

## Usage

```
let phoneNumberSections = [492, 573, 5843]
let phoneNumber = phoneNumberSections.joined(separator: ", ")
print(phoneNumber)
// Prints "492-573-5843"

let numbers = [1, 2, 3, 4, 5, 6, 7]
let sumOfSquares = numbers.joined(separator: " + ") { "\($0 * $0)" }
print(sumOfSquares)
// Prints "1 + 4 + 9 + 16 + 25 + 36 + 49"

struct Person {
    var name: String
    var age: Int
}

let people = [
    Person(name: "Vivien", age: 23),
    Person(name: "Joseph", age: 49),
    Person(name: "Marlon", age: 12)
]

let peopleStr = people.joined(separator: " and ") { "\($0.name) is \($0.age)" }
print(peopleStr)
// Prints "Vivien is 23 and Joseph is 49 and Marlon is 12"
//

```

* * *

### What are everyone's thoughts on adding these joining methods to Swift?

---

<div class="post-metadata">

**Author:** ![cukr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cukr/32/889_2.png) [@cukr](https://forums.swift.org/u/cukr)\
**Post date:** [April 2, 2019, 2:32pm UTC](https://forums.swift.org/t/additional-joined-functions/22518/2 "2019-04-02T14:32:08Z")

</div>

I don't like it. I think that you usually want to be explicit about which way you transform your thing into a string. Using your example of phone number sections - it won't work for sections like 001, so you need to provide a custom transform anyway.

And the second method that you can provide a custom tranform for - I don't think it's better than just writing `foo.map(transform).joined()`

---

<div class="post-metadata">

**Author:** ![AlexanderM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alexanderm/32/9781_2.png) [@AlexanderM](https://forums.swift.org/u/AlexanderM)\
**Post date:** [April 2, 2019, 2:53pm UTC](https://forums.swift.org/t/additional-joined-functions/22518/3 "2019-04-02T14:53:02Z")

</div>

There's many different ways you can combine `map`, `filter`, `reduce`, `dropFirst`, `dropLast`, `prefixWhile`, etc. It doesn't generally make sense to make functions that are combinations of both, because you're going to have an n^2 explosion of pairings that need to be supported.

The code you show is already achievable with `.lazy.map`:

```
import Foundation

let phoneNumberSections = [492, 573, 5843]
let phoneNumber = phoneNumberSections.lazy
	.map(String.init)
	.joined(separator: ", ")
	
print(phoneNumber) // Prints "492-573-5843"

let numbers = [1, 2, 3, 4, 5, 6, 7]
let sumOfSquares = numbers.lazy
	.map { $0 * $0 }
	.map(String.init)
	.joined(separator: " + ")
print(sumOfSquares) // Prints "1 + 4 + 9 + 16 + 25 + 36 + 49"

struct Person {
	var name: String
	var age: Int
}

let people = [
	Person(name: "Vivien", age: 23),
	Person(name: "Joseph", age: 49),
	Person(name: "Marlon", age: 12)
]

let peopleStr = people.lazy
	.map { "\($0.name) is \($0.age)" }
	.joined(separator: " and ")
	
print(peopleStr) // Prints "Vivien is 23 and Joseph is 49 and Marlon is 12"

```
