Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing The value of struct in an array

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.

like image 518
reza23 Avatar asked Oct 14 '14 22:10

reza23


People also ask

Can struct values be changed?

Even though we defined x within the struct as a var property, we cannot change it, because origin is defined using let . This has some major advantages. For example, if you read a line like let point = ... , and you know that point is a struct variable, then you also know that it will never, ever, change.

How do you change a struct to an array?

C = struct2cell( S ) converts a structure into a cell array. The cell array C contains values copied from the fields of S . The struct2cell function does not return field names. To return the field names in a cell array, use the fieldnames function.

Can you store a struct in an array?

A structure may contain elements of different data types – int, char, float, double, etc. It may also contain an array as its member. Such an array is called an array within a structure. An array within a structure is a member of the structure and can be accessed just as we access other elements of the structure.

Can we modify struct in Swift?

The problem is that when you create the struct Swift has no idea whether you will use it with constants or variables, so by default it takes the safe approach: Swift won't let you write methods that change properties unless you specifically request it.


4 Answers

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.

like image 138
Antonio Avatar answered Oct 13 '22 06:10

Antonio


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)
like image 33
LightMan Avatar answered Oct 13 '22 04:10

LightMan


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 }
like image 38
Anton Plebanovich Avatar answered Oct 13 '22 05:10

Anton Plebanovich


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 }
like image 36
Denis Rybkin Avatar answered Oct 13 '22 05:10

Denis Rybkin