Changing The value of struct in an array

ArraysSwiftStruct

Arrays Problem Overview


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.

Arrays Solutions


Solution 1 - Arrays

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.

Solution 2 - Arrays

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)

Solution 3 - Arrays

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 }

Solution 4 - Arrays

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 }

Solution 5 - Arrays

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)

Solution 6 - Arrays

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

Solution 7 - Arrays

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

Solution 8 - Arrays

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
}

Solution 9 - Arrays

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
} 

Solution 10 - Arrays

> Swift 5

Why don't you make your variable var in for loop ?

add var just before your variable name

for (var test) in testings{
    test.value = 3
    print(test.value)
}

you can remove brackets of (var test) also

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionreza23View Question on Stackoverflow
Solution 1 - ArraysAntonioView Answer on Stackoverflow
Solution 2 - ArraysLightManView Answer on Stackoverflow
Solution 3 - ArraysAnton PlebanovichView Answer on Stackoverflow
Solution 4 - ArraysDenis RybkinView Answer on Stackoverflow
Solution 5 - ArraysrintaroView Answer on Stackoverflow
Solution 6 - ArraysmfaaniView Answer on Stackoverflow
Solution 7 - Arraysreza23View Answer on Stackoverflow
Solution 8 - ArraysNicolas ManziniView Answer on Stackoverflow
Solution 9 - ArraysBrianView Answer on Stackoverflow
Solution 10 - ArraysSaifan NadafView Answer on Stackoverflow