20

I am trying to join the elements of a String array via the reduce function. A tried for a bit now, but I can't get what the problem exactly is. This is what I believe should do the trick. I have tried other alternatives too, but given the huge amount I will wait for some input:

var genres = ["towel", "42"]
var jointGenres : String = genres.reduce(0, combine: { $0 + "," + $1 })

Error:

..:14:44: Cannot invoke '+' with an argument list of type '(IntegerLiteralConvertible, combine: (($T6, ($T6, $T7) -> ($T6, $T7) -> $T5) -> ($T6, ($T6, $T7) -> $T5) -> $T5, (($T6, $T7) -> ($T6, $T7) -> $T5, $T7) -> (($T6, $T7) -> $T5, $T7) -> $T5) -> (($T6, ($T6, $T7) -> $T5) -> $T5, (($T6, $T7) -> $T5, $T7) -> $T5) -> $T5)'

From my understanding, $0 should be inferred as a String and $1, by combination with $0, should result as a String too. I don't know what's the deal with the type system here. Any idea?

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
Nicola Miotto
  • 3,647
  • 2
  • 29
  • 43

5 Answers5

30

Your reduce closure should probably look like this:

var jointGenres : String = genres.reduce("", combine: { $0 == "" ? $1 : $0 + "," + $1 })

This has the "" instead of 0 like you had, and makes sure that there is no extra comma in the beginning of the return value.

The original code did not work because the return type that is represented as U in documentation was originally 0 in your answer, while you are trying to add a String to it. In your case, you really want both U and T to represent Strings instead of Ints.

erdekhayser
  • 6,537
  • 2
  • 37
  • 69
24

reduce is not a straightforward solution here since you need special handling for the first element. String's join method is better for this purpose:

let strings = ["a", "b", "c"]
let joinedString = ",".join(strings)

If you know the array is not empty there is another possible solution with reduce that also avoids conditionals:

let joinedStrings = strings[1..<strings.count].reduce(strings[0]) { $0 + "," + $1 }
Mihai Damian
  • 11,193
  • 11
  • 59
  • 81
9

Cocoa already has a function to do this. It is marred by needing a typecast to NSArray.

var genres = ["towel", "42"]
var joinGenres = (genres as NSArray).componentsJoinedByString(",")

To my surprise, this function also can be applied to arrays of types other than String:

let ints = [1,5,9,15,29]
let listOfInts = (ints as NSArray).componentsJoinedByString(",")
AlexT
  • 596
  • 7
  • 15
8

If using Swift 4:

var jointGenres:String = genres.joined(separator: ",")
Jake Lee
  • 7,549
  • 8
  • 45
  • 86
Lukas Mohs
  • 290
  • 3
  • 4
2

The problem is your first argument to reduce. This is an accumulator, it's an integer literal, and it's what's passed as $0 on the first run of the block. You're asking the reduce function to add a string to this.

Instead of 0 as the accumulator argument, you should be passing "", an empty string.

This works:

var genres = ["towel", "42"]
var jointGenres : String = genres.reduce("", combine: { $0 + "," + $1 })
iluvcapra
  • 9,436
  • 2
  • 30
  • 32