In Sequence/Collection Enhancements, the possibility of an in-place map was discussed. I found myself needing this today, and I figured (similar to toggle
on Bool
) it might be a useful addition to the standard library. Here's my implementation:
extension MutableCollection {
mutating func mapInPlace(_ x: (inout Element) -> ()) {
for i in indices {
x(&self[i])
}
}
}
This is handy when you want to mutate some type in-place, rather than creating an entirely new value. Consider the following boiled-down example from Point Free:
struct Food {
var name: String
}
struct User {
var foods: [Food]
var name: String
}
If we want to change all the food elements a user has (for example, by appending: "& Salad"), we could write it with map
:
extension User {
mutating func healthierMap() {
foods = foods.map { x in
var copy = x
copy.name += "& Salad"
return copy
}
}
}
The copy
is annoying, distracts from the essence of the code, and is inefficient. Furthermore, the entire array of foods
is copied.
Using a for
loop is much shorter, but requires us to think about indices:
extension User {
mutating func healthierFor() {
for i in foods.indices {
foods[i].name += "& Salad"
}
}
}
Finally, the solution using mapInPlace
is the shortest, and as efficient as the for
solution:
extension User {
mutating func healthier() {
foods.mapInPlace { $0.name += " & Salad" }
}
}
(I don't know if mapInPlace
is the best name yet, very much open to bike-shedding).