Is it right make map() with captured variable?

Some time I need pass external variable to the map() like next code:

    let tomap = ["a", "d", "l", "m"]
    var currentNumber = 10
    let mapped = tomap.map { letter -> String in 
        var letter = letter
        letter += "\(currentNumber)"
        currentNumber += 10
        return letter
    }

currentNumber was captured by map closure. The question is: Is it correct way to use mapping?

Would it do what you want? Yes.
but using non-pure functions for map feels wrong for me and probably most people that like using map

I would do it like this instead:

let mapped = zip(tomap, sequence(first: 10, next: { $0 + 10 })).map {
    $0 + "\($1)"
}
3 Likes

Yes, it is a perfectly correct way to use map. Capturing variables is a basic and fundamental part of how closures work, and if that’s what you want to do, they are the right tool for the job.

I will note that you can make your example shorter by using defer:

let mapped: String = tomap.map {
  defer { currentNumber += 10 }
  return $0 + "\(currentNumber)"
}
4 Likes

Also, to start with -1 step is a pretty common technique to simplify the loop:

var currentNumber = 0
let mapped = tomap.map { letter -> String in 
  currentNumber += 10
  return letter + "\(currentNumber)"
}

If you want to stop your functional purist friends from squirming, and want to edit in place, you could also use for loop:

var tomap = ...
var currentNumber = 0
for index in tomap.indices {
  currentNumber += 10
  tomap[index] += "\(currentNumber)"
}
2 Likes