Question: How to join array of optional integers to string?

Question

How to join array of optional integers to string?

Answers 5
Added at 2017-01-05 16:01
Tags
Question

Given [Int?], need to build string from it.

This code snippet works

    let optionalInt1: Int? = 1
    let optionalInt2: Int? = nil

    let unwrappedStrings = [optionalInt1, optionalInt2].flatMap({ $0 }).map({ String($0) })
    let string = unwrappedStrings.joined(separator: ",")

But I don't like flatMap followed by map. Is there any better solution?

Answers to

How to join array of optional integers to string?

nr: #1 dodano: 2017-01-05 17:01

If you don't like the flatMap and the map together you can replace this

[optionalInt1, optionalInt2].flatMap({ $0 }).map({ String($0) })

with this

[optionalInt1, optionalInt2].flatMap { $0?.description }

Wrap up

let optionalInt1: Int? = 1
let optionalInt2: Int? = nil

let result = [optionalInt1, optionalInt2]
    .flatMap { $0?.description }
    .joined(separator: ",")

Update

Since:

  • as @Hamish pointed out direct access to description is discouraged by the Swift team
  • and the OP wants to avoid the flatMap + map concatenation because of the double loop

I propose another solution

let result = [optionalInt1, optionalInt2].flatMap {
        guard let num = $0 else { return nil }
        return String(num)
    }.joined(separator: ",")
nr: #2 dodano: 2017-01-05 17:01

Here's another approach:

[optionalInt1, optionalInt2].flatMap { $0 == nil ? nil : String($0!) }
nr: #3 dodano: 2017-01-05 17:01

You can make use of the map method of Optional within a flatMap closure applied to the array, making use of the fact that the former will return nil without entering the supplied closure in case the optional itself is nil:

let unwrappedStrings = [optionalInt1, optionalInt2]
    .flatMap { $0.map(String.init) }

Also, if you don't wrap the trailing closures (of flatMap, map) in paranthesis and furthermore make use of the fact that the initializer reference String.init will (in this case) non-ambigously resolve to the correct String initializer (as used above), a chained flatMap and map needn't look "bloated", and is also a fully valid approach here (the chained flatMap and map also hold value for semantics).

let unwrappedStrings = [optionalInt1, optionalInt2]
    .flatMap{ $0 }.map(String.init) 
nr: #4 dodano: 2017-01-05 19:01

Avoiding flatMap and map altogether. However creating a local var.

let optionalInt1: Int? = 1
let optionalInt2: Int? = nil
let optionalInts = [optionalInt1, optionalInt2]

var strings = [String]()
for optInt in optionalInts {
    if let integer = optInt {
        strings.append(String(integer))
    }
}
let string = strings.joined(separator: ",")
nr: #5 dodano: 2017-01-05 20:01

The other answers use flatMap to eliminate the nil elements and require a call to joined() to combine the elements and add the commas.

Using joined() to add the commas is of course a second pass through the array. Here's a way to use reduce to do this in one pass:

let arr: [Int?] = [nil, nil, 1, 2, 3, nil, 4, nil, 5, nil]

let result = arr.reduce("") { $1 == nil ? $0 : $0.isEmpty ? "\($1!)" : $0 + ",\($1!)" }

print(result)

Output:

1,2,3,4,5

Explanation

This example uses reduce to construct the result string element by element.

reduce is a method called on a sequence. It takes an initial value ("" in this example) and a closure that combines the next element in the sequence with the partial result to create the next partial result.

The closure is called on each element in the sequence (i.e. array arr). The closure first checks if the element is nil and if it is, it returns the partial result unmodified. If the element is not nil, it then checks if the partial result is still the empty string. If it is empty, this is the first element in the result so it returns a string made up of just the element. If the partial result is not empty, it adds the new element preceded by a , to the partial result.

Source Show
◀ Wstecz