13

I created a simple struct called ShoppingList.

struct ShoppingList {

    var shoppingListId :NSNumber
    var title :String
    var groceryItems :[GroceryItem]

    init() {
        self.title = ""
        self.groceryItems = [GroceryItem]()
        self.shoppingListId = NSNumber(integer: 0)
    }

}

Next I created a ShoppingList array like this:

 var shoppingLists = [ShoppingList]()

After that I fetch the shopping lists etc. Now, I iterate through the shoppingLists and change the title but it ever updates the title property.

 for var shoppingList in shoppingLists {
  let items = getGroceryItemsByShoppingList(shoppingList)
    shoppingList.groceryItems = getGroceryItemsByShoppingList(shoppingList)
    shoppingList.title = "BLAH" // copied by value
    print("ShoppingList \(shoppingList.title) has \(shoppingList.groceryItems.count) items") // THIS PRINT BLAH
}

print("shoppingLists[0].groceryItems.count \(shoppingLists[0].groceryItems.count)") // THIS PRINTS THE ORIGINAL CONTENT

I believe that when I am running the loop it is copying by value and hence the original array is never changed. How can I change the original array using For loop?

1
  • Thank you for posting a clear and concise question, you've renewed my faith in developers. Commented Dec 16, 2015 at 17:04

2 Answers 2

13

There are two approaches I would use here. The first approach is to reconsider whether ShoppingList is a value type or a reference type. The fact that it has an identifier suggests to me that it's really a reference type. If two shopping lists have the same contents, should the be considered the same list? I suspect not. What would it mean to have two lists that have the same identifier, but different contents? If that's illegal, again, that tends to point to it being a reference type because it has an identity.

If it's a reference type, make it a final class:

final class ShoppingList {}

Final classes preserve the simplicity of structs because they do not suffer the problems of inheritance. But they provide reference semantics. With that change, your original code would work.

The other way to approach this is more functional, where everything is a value. In that case, you can achieve this by mapping copies of your shopping lists:

shoppingLists = shoppingLists.map { list in
    var newList = list
    newList.groceryItems = getGroceryItemsByShoppingList(list)
    return newList
}

This pushes us towards a more functional approach, but it makes the identifier awkward. So if you really wanted to go this way, I'd want to get rid of identifiers and maybe even make shopping lists immutable. In that case, any two identical shopping lists are the same list, and you can write in a more functional style.

But I suspect that making ShoppingList a reference type is your better approach.

Sign up to request clarification or add additional context in comments.

2 Comments

I really like your argument on value vs ref types. Since the shopping list has an identify (shoppingListId) it is better to have it as a ref type .. as you suggested.
perfect ! it helps me a lot.
6

Alright, I figured this out. It seems that the way you do this now is by making a reference and changing its properties inside of the loop (which does not change an original instance in case of structures). What you should do instead is call them directly like this:

for index in 0..<shoppingLists.count {

//Stuff you do. Do the same way, just replace "shoppingList" to "shoppingLists[index]"

shoppingLists[index].title = "BLAH" // copied by value
    print("ShoppingList \(shoppingLists[index].title) has items") // THIS PRINT BLAH

}

print("shoppingLists[0].groceryItems.title: \(shoppingLists[0].title)") // Returns "shoppingLists[0].groceryItems.title: BLAH"

This works, I checked, you're welcome!

4 Comments

If you omit the "var" then you are treating the instance was "let" which means constant and you cannot change the constant.
Not if it is a struct!
Wait, where exactly in your code you check the title? Because outside the loop, you never check it again
Thanks! Of course it works since you are using indexes to access the element. I was talking about using for loop without indexes. Kind of like foreach loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.