Reputation: 2956
I want to be able to modify my array of objects using map
in Swift of the fly, without looping through each element.
Before here were able to do something like this (Described in more details here:
gnomes = gnomes.map { (var gnome: Gnome) -> Gnome in
gnome.age = 140
return gnome
}
Thanks for Erica Sadun and others, new proposals have gone through and we're now getting rid of C-style loops and using var
inside the loop.
In my case I'm first getting a warning to remove the var
in then an error my gnome
is a constant (naturally)
My question is : How do we alter arrays inside a map
or the new styled loops for that matter to be fully prepared for Swift 3.0?
Upvotes: 17
Views: 22791
Reputation: 373
Struct
objects are immutable when received as a function param or when stored in a list.
For tackling your specific case you will first have to make the mutable copy of the object and then update its state. Then, you must replace the old object with the mutated copy.
Builder pattern as demonstrated below can be used to do this more elegantly.
struct DummyModel {
var state: Bool = false
func withState(_ state: Bool) -> Self {
var mutableCopy = self
mutableCopy.state = state
return mutableCopy
}
}
var models = [
DummyModel(),
DummyModel(),
DummyModel(),
DummyModel(),
]
print(models) // All prints false
models = models.map { $0.withState(true) }
print(models) // All will print true
Upvotes: 0
Reputation: 2758
Given:
struct Gnome {
var age: Int = 0
}
var gnomes = Array(count: 5, repeatedValue: Gnome())
... there are two decent options. The first is as @vadian put it:
gnomes = gnomes.map{
var gnome = $0
gnome.age = 70
return gnome
}
Whilst the second keeps control over "ageing" private
and simplifies mapping at the point of call:
struct Gnome {
private(set) var age: Int = 0
func aged(age: Int) -> Gnome {
var gnome = self
gnome.age = age
// any other ageing related changes
return gnome
}
}
gnomes = gnomes.map{ $0.aged(140) }
Of course, reference types still have their place in programming, which may well be a better fit in this case. The friction we are experiencing here suggests that we are trying to treat these structures as if they were objects. If that is the behaviour you need, then you should consider implementing Gnome
as a class
.
Upvotes: 2
Reputation: 73186
(Below follows the case where Gnome
is a reference type; a class -- since you haven't showed us how you've defined Gnome
. For the case where Gnome
as value type (a struct), see @vadian:s answer)
The removal of var
will not effect using .map
to mutate mutable members of an array of reference type objects. I.e., you could simply use your old approach (omitting however, the var
in the .map
closure signature).
class Gnome {
var age = 42
}
var gnomes = [Gnome(), Gnome(), Gnome()]
gnomes = gnomes.map {
$0.age = 150
return $0
}
/* result */
gnomes.forEach { print($0.age) } // 3x 150
However, in case you just want to modify your original array rather than assigning the result of .map
to a new array, .forEach
might be a more appropriate choice than .map
.
gnomes.forEach { $0.age = 140 }
/* result */
gnomes.forEach { print($0.age) } // 3x 140
Upvotes: 18
Reputation: 285082
If you want to keep that syntax, just use a (mutable) temporary variable
gnomes = gnomes.map { (gnome: Gnome) -> Gnome in
var mutableGnome = gnome
mutableGnome.age = 140
return mutableGnome
}
Upvotes: 37