I am still not sure about the rules of struct copy or reference.
I want to mutate a struct object while iterating on it from an array:
For instance in this case I would like to change the background color
but the compiler is yelling at me
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [MyStruct]
...
for obj in arrayOfMyStruct {
obj.backgroundColor = UIColor.redColor() // ! get an error
}
struct are value types, thus in the for loop you are dealing with a copy.
Just as a test you might try this:
Swift 3:
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options]()
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
Swift 2:
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [Options]()
for (index, _) in enumerate(arrayOfMyStruct) {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
Here you just enumerate the index, and access directly the value stored in the array.
Hope this helps.
You can use use Array.indices:
for index in arrayOfMyStruct.indices {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
You are working with struct objects which are copied to local variable when using for in loop. Also array is a struct object, so if you want to mutate all members of the array, you have to create modified copy of original array filled by modified copies of original objects.
arrayOfMyStruct = arrayOfMyStruct.map { obj in
var obj = obj
obj.backgroundColor = .red
return obj
}
It can be simplified by adding this Array extension.
Swift 4
extension Array {
mutating func mutateEach(by transform: (inout Element) throws -> Void) rethrows {
self = try map { el in
var el = el
try transform(&el)
return el
}
}
}
Usage
arrayOfMyStruct.mutateEach { obj in
obj.backgroundColor = .red
}
For Swift 3, use the enumerated() method.
For example:
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
The tuple also includes a copy of the object, so you could use for (index, object) instead to get to the object directly, but since it's a copy you would not be able to mutate the array in this way, and should use the index to do so. To directly quote the documentation:
If you need the integer index of each item as well as its value, use
the enumerated() method to iterate over the array instead. For each
item in the array, the enumerated() method returns a tuple composed of
an integer and the item.
Another way not to write subscript expression every time.
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options(), Options(), Options()]
for index in arrayOfMyStruct.indices {
var option: Options {
get { arrayOfMyStruct[index] }
set { arrayOfMyStruct[index] = newValue }
}
option.backgroundColor = .red
}
I saw this method in some code and it seems to be working
for (var mutableStruct) in arrayOfMyStruct {
mutableStruct.backgroundColor = UIColor.redColor()
}
Swift 4.2
I have multiple functions that replace an object or struct in an array if it exists, and if it does not exist, it adds it.
func updateFruit(_ fruit: Fruit)
{
if let idx = fruitArray.firstIndex(where: { $0.id == fruit.id })
{
fruitArray[idx] = fruit
}
else
{
fruitArray.append(fruit)
}
}
Obviously I could make this into extension on Array:
extension Array
{
mutating func replaceOrAppend(_ item: Element, whereFirstIndex predicate: (Element) -> Bool)
{
if let idx = self.firstIndex(where: predicate)
{
self[idx] = item
}
else
{
append(item)
}
}
}
However, is there a simpler, easier way of expressing this? Preferably using a closure or build-in function.
NOTE: current implementation does not allow using a set.
Given your use case, in which you're always checking $0.<prop> == newthing.<prop>, you can lift this a little more by adding:
mutating func replaceOrAppend<Value>(_ item: Element,
firstMatchingKeyPath keyPath: KeyPath<Element, Value>)
where Value: Equatable
{
let itemValue = item[keyPath: keyPath]
replaceOrAppend(item, whereFirstIndex: { $0[keyPath: keyPath] == itemValue })
}
You can then use it like:
struct Student {
let id: Int
let name: String
}
let alice0 = Student(id: 0, name: "alice")
let alice1 = Student(id: 1, name: "alice")
let bob = Student(id: 0, name: "bob")
var array = [alice0]
array.replaceOrAppend(alice1, firstMatchingKeyPath: \.name) // [alice1]
array.replaceOrAppend(bob, firstMatchingKeyPath: \.name) // [alice1, bob]
And of course if you do this a lot, you can keep lifting and lifting.
protocol Identifiable {
var id: Int { get }
}
extension Student: Identifiable {}
extension Array where Element: Identifiable {
mutating func replaceOrAppendFirstMatchingID(_ item: Element)
{
replaceOrAppend(item, firstMatchingKeyPath: \.id)
}
}
array.replaceOrAppendFirstMatchingID(alice0) // [alice1, alice0]
I can suggest to create protocol Replacable with replaceValue that will represent identifier which we can use to enumerate thru objects.
protocol Replacable {
var replaceValue: Int { get }
}
now we can create extension to Array, but now we can drop predicate from example code like this
extension Array where Element: Replacable {
mutating func replaceOrAppend(_ item: Element) {
if let idx = self.firstIndex(where: { $0.replaceValue == item.replaceValue }) {
self[idx] = item
}
else {
append(item)
}
}
}
Since Set is not ordered collection, we can simply remove object if set contains it and insert new value
extension Set where Element: Replacable {
mutating func replaceOrAppend(_ item: Element) {
if let existItem = self.first(where: { $0.replaceValue == item.replaceValue }) {
self.remove(existItem)
}
self.insert(item)
}
}
Assuming your Types are Equatable, this is a generic extension:
extension RangeReplaceableCollection where Element: Equatable {
mutating func addOrReplace(_ element: Element) {
if let index = self.firstIndex(of: element) {
self.replaceSubrange(index...index, with: [element])
}
else {
self.append(element)
}
}
}
Though, keep in mind my (and your) function will only replace one of matching items.
Full Working playground test:
I'm writing a Swift extension that checks if two or more CGPoints in array have the same coordinates. Having this code I can check all points in array.
But how to check just several elements (not all)?
Here's the extension...
import Foundation
extension Array where Element : Equatable {
func equalCoordinates() -> Bool {
if let firstElement = first {
return dropFirst().contains { $0 == firstElement }
}
return true
}
}
If two (or more) red CGPoints have identical coordinates they must be turned into green ones.
...and a code in ViewController using equalCoordinates() method:
func drawn() {
let colorArray = array.map { $0.pointCoord()[0] }
for dot in array {
for cPoint in dot.pointCoord() {
if colorArray.equalCoordinates() {
let altColor = dot.alternativePointColour()
draw(cPoint, color: altColor)
} else {
let color = dot.pointColour()
draw(cPoint, color: color)
}
}
}
}
...........
Swift.print(colorArray.equalCoordinates())
...........
With absolutely no concern given to efficiency (that can be improved depending on the size of your data), this is how I'd probably go about it. Each piece is pretty simple, so you should be able to adapt it to a wide variety of different outputs (if you prefer something other than IndexSet for instance).
import Foundation
import CoreGraphics
// We could put this on Collection rather than Array, but then we'd have to rewrite
// IndexSet on generic indices or use [Index].
extension Array where Element : Equatable {
func uniqueElements() -> [Element] {
// This is O(n^2), but it's hard to beat that without adding either
// Hashable (for Set) or Comparable (to pre-sort) to the requirements,
// neither of which CGPoints have by default.
var uniqueElements: [Element] = []
for element in self {
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
return uniqueElements
}
func indexSet(of element: Element) -> IndexSet {
var indices = IndexSet()
for (index, member) in enumerated() {
if element == member {
indices.insert(index)
}
}
return indices
}
func indexSetsGroupedByEquality() -> [(element: Element, indexSet: IndexSet)] {
return uniqueElements().map { element in (element, indexSet(of: element)) }
}
func indexSetsOfCollidingElements() -> [IndexSet] {
func hasCollisions(_: Element, indexSet: IndexSet) -> Bool { return indexSet.count > 1 }
return indexSetsGroupedByEquality()
.filter(hasCollisions)
.map { $0.indexSet }
}
}
let points = [
CGPoint(x:1,y:1),
CGPoint(x:2,y:1),
CGPoint(x:1,y:1),
CGPoint(x:3,y:1),
CGPoint(x:2,y:1),
]
print(points.indexSetsOfCollidingElements().map(Array.init))
// [[0, 2], [1, 4]]
Swift 2.2 version
extension Array where Element : Equatable {
func uniqueElements() -> [Element] {
var uniqueElements: [Element] = []
for element in self {
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
return uniqueElements
}
func indexSet(of element: Element) -> NSIndexSet {
let indices = NSIndexSet()
for (index, member) in enumerate() {
if element == member {
indices.insertValue(index, inPropertyWithKey: "")
}
}
return indices
}
func indexSetsGroupedByEquality() -> [(element: Element,
indexSet: NSIndexSet)] {
return uniqueElements().map { element in
(element, indexSet(of: element))
}
}
func indexSetsOfCollidingElements() -> [NSIndexSet] {
func hasCollisions(_: Element, indexSet: NSIndexSet) -> Bool {
return indexSet.count > 0
}
return indexSetsGroupedByEquality().filter(hasCollisions)
.map { $0.indexSet }
}
}
Consider the array [1,2,3,4]. How can I rearrange the array item to new position.
For example:
put 3 into position 4 [1,2,4,3]
put 4 in to position 1 [4,1,2,3]
put 2 into position 3 [1,3,2,4].
Swift 3.0+:
let element = arr.remove(at: 3)
arr.insert(element, at: 2)
and in function form:
func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
var arr = array
let element = arr.remove(at: fromIndex)
arr.insert(element, at: toIndex)
return arr
}
Swift 2.0:
This puts 3 into position 4.
let element = arr.removeAtIndex(3)
arr.insert(element, atIndex: 2)
You can even make a general function:
func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
var arr = array
let element = arr.removeAtIndex(fromIndex)
arr.insert(element, atIndex: toIndex)
return arr
}
The var arr is needed here, because you can't mutate the input parameter without specifying it to be in-out. In our case however we get a pure functions with no side effects, which is a lot easier to reason with, in my opinion.
You could then call it like this:
let arr = [1,2,3,4]
rearrange(arr, fromIndex: 2, toIndex: 0) //[3,1,2,4]
All great answers! Here's a more complete Swift 5 solution with performance in mind and bonus for benchmark and GIF fans. ✌️
extension Array where Element: Equatable
{
mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
}
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
// Don't work for free and use swap when indices are next to each other - this
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
edit/update: Swift 3.x
extension RangeReplaceableCollection where Indices: Equatable {
mutating func rearrange(from: Index, to: Index) {
precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indices")
insert(remove(at: from), at: to)
}
}
var numbers = [1,2,3,4]
numbers.rearrange(from: 1, to: 2)
print(numbers) // [1, 3, 2, 4]
nice tip from Leo.
for Swift 3...5.5:
extension Array {
mutating func rearrange(from: Int, to: Int) {
insert(remove(at: from), at: to)
}
}
var myArray = [1,2,3,4]
myArray.rearrange(from: 1, to: 2)
print(myArray)
var arr = ["one", "two", "three", "four", "five"]
// Swap elements at index: 2 and 3
print(arr)
arr.swapAt(2, 3)
print(arr)
Swift 5
extension Array where Element: Equatable {
mutating func move(_ item: Element, to newIndex: Index) {
if let index = index(of: item) {
move(at: index, to: newIndex)
}
}
mutating func bringToFront(item: Element) {
move(item, to: 0)
}
mutating func sendToBack(item: Element) {
move(item, to: endIndex-1)
}
}
extension Array {
mutating func move(at index: Index, to newIndex: Index) {
insert(remove(at: index), at: newIndex)
}
}
We can use swap method to swap items in an array :
var arr = ["one", "two", "three", "four", "five"]
// Swap elements at index: 2 and 3
print(arr)
swap(&arr[2], &arr[3])
print(arr)
#ian has provided good solution but it will be crash when array become out of bound added check for that too
extension Array where Element: Equatable {
public mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = index(of: element) {
self.move(from: oldIndex, to: newIndex)
}
}
public mutating func moveToFirst(item: Element) {
self.move(item, to: 0)
}
public mutating func move(from oldIndex: Index, to newIndex: Index) {
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
// Index out of bound handle here
if newIndex >= self.count { return }
// Don't work for free and use swap when indices are next to each other - this
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
// Remove at old index and insert at new location
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
There is no move functionality in swift for arrays. you can take an object at an index by removing it from there and place it in your favourite index by using 'insert'
var swiftarray = [1,2,3,4]
let myobject = swiftarray.removeAtIndex(1) // 2 is the object at 1st index
let myindex = 3
swiftarray.insert(myobject, atIndex: myindex) // if you want to insert the object to a particular index here it is 3
swiftarray.append(myobject) // if you want to move the object to last index
Swift 4 - Solution for moving a group of items from an IndexSet of indices, grouping them and moving them to a destination index. Realised through an extension to RangeReplaceableCollection. Includes a method to remove and return all items in an IndexSet. I wasn't sure how to constrain the extension to a more generalised form than to constrain the element than integer while maintaining the ability to construct IndexSets as my knowledge of Swift Protocols is not that extensive.
extension RangeReplaceableCollection where Self.Indices.Element == Int {
/**
Removes the items contained in an `IndexSet` from the collection.
Items outside of the collection range will be ignored.
- Parameter indexSet: The set of indices to be removed.
- Returns: Returns the removed items as an `Array<Self.Element>`.
*/
#discardableResult
mutating func removeItems(in indexSet: IndexSet) -> [Self.Element] {
var returnItems = [Self.Element]()
for (index, _) in self.enumerated().reversed() {
if indexSet.contains(index) {
returnItems.insert(self.remove(at: index), at: startIndex)
}
}
return returnItems
}
/**
Moves a set of items with indices contained in an `IndexSet` to a
destination index within the collection.
- Parameters:
- indexSet: The `IndexSet` of items to move.
- destinationIndex: The destination index to which to move the items.
- Returns: `true` if the operation completes successfully else `false`.
If any items fall outside of the range of the collection this function
will fail with a fatal error.
*/
#discardableResult
mutating func moveItems(from indexSet: IndexSet, to destinationIndex: Index) -> Bool {
guard indexSet.isSubset(of: IndexSet(indices)) else {
debugPrint("Source indices out of range.")
return false
}
guard (0..<self.count + indexSet.count).contains(destinationIndex) else {
debugPrint("Destination index out of range.")
return false
}
let itemsToMove = self.removeItems(in: indexSet)
let modifiedDestinationIndex:Int = {
return destinationIndex - indexSet.filter { destinationIndex > $0 }.count
}()
self.insert(contentsOf: itemsToMove, at: modifiedDestinationIndex)
return true
}
}
Here's a solution with functions to both change the array in-place and to return a changed array:
extension Array {
func rearranged(from fromIndex: Int, to toIndex: Int) -> [Element] {
var arr = self
let element = arr.remove(at: fromIndex)
if toIndex >= self.count {
arr.append(element)
} else {
arr.insert(element, at: toIndex)
}
return arr
}
mutating func rearrange(from fromIndex: Int, to toIndex: Int) {
let element = self.remove(at: fromIndex)
if toIndex >= self.count {
self.append(element)
} else {
self.insert(element, at: toIndex)
}
}
}
Since macOS 10.15, iOS 14, MutableCollection has the method move(fromOffsets:toOffset:).
https://developer.apple.com/documentation/swift/mutablecollection/move(fromoffsets:tooffset:)
Update with Swift 4,
Swipe array index
for (index,addres) in self.address.enumerated() {
if addres.defaultShipping == true{
let defaultShipping = self.address.remove(at: index)
self.address.insert(defaultShipping, at: 0)
}
}
Efficient solution:
extension Array
{
mutating func move(from sourceIndex: Int, to destinationIndex: Int)
{
guard
sourceIndex != destinationIndex
&& Swift.min(sourceIndex, destinationIndex) >= 0
&& Swift.max(sourceIndex, destinationIndex) < count
else {
return
}
let direction = sourceIndex < destinationIndex ? 1 : -1
var sourceIndex = sourceIndex
repeat {
let nextSourceIndex = sourceIndex + direction
swapAt(sourceIndex, nextSourceIndex)
sourceIndex = nextSourceIndex
}
while sourceIndex != destinationIndex
}
}
func adjustIndex(_ index: Int, forRemovalAt removed: Int) -> Int {
return index <= removed ? index : index - 1
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
insert(remove(at: oldIndex), at: adjustIndex(newIndex, forRemovalAt: oldIndex))
}
}
Swift 5 Tested
Just to add extra toppings on cake,
I've added functionality to handle Array<Dictionary<String,Any>>
Main Source of my answer here https://stackoverflow.com/a/50205000/4131763,
here is my version,
//Array+Extension.swift,
extension Array where Element: Equatable
{
mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
}
}
extension Array where Element == Dictionary<String, Any> {
mutating func move(_ element:Element, to newIndex: Index) {
if let oldIndex = self.firstIndex(where: { ($0.keys.first ?? "") == (element.keys.first ?? "") }) {
self.move(from: oldIndex, to: newIndex)
}
}
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
// Don't work for free and use swap when indices are next to each other - this
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
HOW TO USE,
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "MEDICALNOTICEREQUIRED" }) {
let obj = array[oldIndex]
array.move(obj, to: array.startIndex)
}
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "HIGHRISKCONFIRMATION" }) {
let obj = array[oldIndex]
let oldIndexMEDICALNOTICEREQUIRED = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "MEDICALNOTICEREQUIRED" })!
array.move(obj, to: oldIndexMEDICALNOTICEREQUIRED + 1)
}
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "UNLICENCEDCONFIRMATION" }) {
let obj = array[oldIndex]
let oldIndexHIGHRISKCONFIRMATION = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "HIGHRISKCONFIRMATION" })!
array.move(obj, to: oldIndexHIGHRISKCONFIRMATION + 1)
}
Leo Dabus's solution is great however using precondition(from != to && indices.contains(from != to && indices.contains(to), "invalid indexes"), will crash the app if the conditions are not met. I changed it to guard and an if statement - if for some reason the conditions are not met, nothing happens and the app continues. I think we should avoid making extensions that may crash the app. If you wish you could make the rearrange function return a Bool - true if successful and false if failed.
The safer solution:
extension Array {
mutating func rearrange(from: Int, to: Int) {
guard from != to else { return }
//precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indexes")
if indices.contains(from) && indices.contains(to) {
insert(remove(at: from), at: to)
}
}
Function(not swift but universal.. lookup/remove/insert):
func c_move_to(var array:Array,var from:Int,var to:Int):
var val = array[from]
array.remove(from)
array.insert(to,val)
return array
How to use:
print("MOVE 0 to 3 [1,2,3,4,5]" , c_move_to([1,2,3,4,5],0,3))
print("MOVE 1 to 2 [1,2,3,4,5]" , c_move_to([1,2,3,4,5],1,2))
spits out:
MOVE 0 to 3 [1,2,3,4,5][2, 3, 4, 1, 5]
MOVE 1 to 2 [1,2,3,4,5][1, 3, 2, 4, 5]
How about this solution?
The element to be changed and the element to be changed have been changed.
// Extenstion
extension Array where Element: Equatable {
mutating func change(_ element: Element, to newIndex: Index) {
if let firstIndex = self.firstIndex(of: element) {
self.insert(element, at: 0)
self.remove(at: firstIndex + 1)
}
}
}
// Example
var testArray = ["a", "b", "c", "EE", "d"]
testArray.change("EE", to: 0)
// --> Result
// ["EE", "a", "b", "c", "d"]
I want to store structs inside an array, access and change the values of the struct in a for loop.
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
test1.value = 2
// this works with no issue
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
for test in testings{
test.value = 3
// here I get the error:"Can not assign to 'value' in 'test'"
}
If I change the struct to class it works. Can anyone tell me how I can change the value of the struct.
Besides what said by #MikeS, remember that structs are value types. So in the for loop:
for test in testings {
a copy of an array element is assigned to the test variable. Any change you make on it is restricted to the test variable, without doing any actual change to the array elements. It works for classes because they are reference types, hence the reference and not the value is copied to the test variable.
The proper way to do that is by using a for by index:
for index in 0..<testings.count {
testings[index].value = 15
}
in this case you are accessing (and modifying) the actual struct element and not a copy of it.
Well I am going to update my answer for swift 3 compatibility.
When you are programming many you need to change some values of objects that are inside a collection. In this example we have an array of struct and given a condition we need to change the value of a specific object. This is a very common thing in any development day.
Instead of using an index to determine which object has to be modified I prefer to use an if condition, which IMHO is more common.
import Foundation
struct MyStruct: CustomDebugStringConvertible {
var myValue:Int
var debugDescription: String {
return "struct is \(myValue)"
}
}
let struct1 = MyStruct(myValue: 1)
let struct2 = MyStruct(myValue: 2)
let structArray = [struct1, struct2]
let newStructArray = structArray.map({ (myStruct) -> MyStruct in
// You can check anything like:
if myStruct.myValue == 1 {
var modified = myStruct
modified.myValue = 400
return modified
} else {
return myStruct
}
})
debugPrint(newStructArray)
Notice all the lets, this way of development is safer.
The classes are reference types, it's not needed to make a copy in order to change a value, like it happens with structs. Using the same example with classes:
class MyClass: CustomDebugStringConvertible {
var myValue:Int
init(myValue: Int){
self.myValue = myValue
}
var debugDescription: String {
return "class is \(myValue)"
}
}
let class1 = MyClass(myValue: 1)
let class2 = MyClass(myValue: 2)
let classArray = [class1, class2]
let newClassArray = classArray.map({ (myClass) -> MyClass in
// You can check anything like:
if myClass.myValue == 1 {
myClass.myValue = 400
}
return myClass
})
debugPrint(newClassArray)
To simplify working with value types in arrays you could use following extension (Swift 3):
extension Array {
mutating func modifyForEach(_ body: (_ index: Index, _ element: inout Element) -> ()) {
for index in indices {
modifyElement(atIndex: index) { body(index, &$0) }
}
}
mutating func modifyElement(atIndex index: Index, _ modifyElement: (_ element: inout Element) -> ()) {
var element = self[index]
modifyElement(&element)
self[index] = element
}
}
Example usage:
testings.modifyElement(atIndex: 0) { $0.value = 99 }
testings.modifyForEach { $1.value *= 2 }
testings.modifyForEach { $1.value = $0 }
How to change Array of Structs
for every element:
itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }
for specific element:
itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
.forEach { itemsArray[$0].someValue = newValue }
You have enough of good answers. I'll just tackle the question from a more generic angle.
As another example to better understand value types and what it means they get copied:
struct Item {
var value:Int
}
func change (item: Item, with value: Int){
item.value = value // cannot assign to property: 'item' is a 'let' constant
}
That is because item is copied, when it comes in, it is immutable — as a convenience.
Had you made Item a class type then you were able to change its value.
var item2 = item1 // mutable COPY created
item2.value = 10
print(item2.value) // 10
print(item1.value) // 5
This is very tricky answer. I think, You should not do like this:
struct testing {
var value:Int
}
var test1 = testing(value: 6)
var test2 = testing(value: 12)
var ary = [UnsafeMutablePointer<testing>].convertFromArrayLiteral(&test1, &test2)
for p in ary {
p.memory.value = 3
}
if test1.value == test2.value {
println("value: \(test1.value)")
}
For Xcode 6.1, array initialization will be
var ary = [UnsafeMutablePointer<testing>](arrayLiteral: &test1, &test2)
It is possible to use the map function to get this effect - essentially creating a new array
itemsArray = itemsArray.map {
var card = $0
card.isDefault = aCard.token == token
return card
}
I ended up recreating a new array of struct see the example below.
func updateDefaultCreditCard(token: String) {
var updatedArray: [CreditCard] = []
for aCard in self.creditcards {
var card = aCard
card.isDefault = aCard.token == token
updatedArray.append(card)
}
self.creditcards = updatedArray
}
I tried Antonio's answer which seemed quite logical but to my surprise it does not work. Exploring this further I tried the following:
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
var test1b = testings[0]
test1b.value = 13
// I would assume this is same as test1, but it is not test1.value is still 6
// even trying
testings[0].value = 23
// still the value of test1 did not change.
// so I think the only way is to change the whole of test1
test1 = test1b