Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing Object's Properties Inside For Loop in Swift

Tags:

ios

swift

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?

like image 551
john doe Avatar asked Dec 16 '15 16:12

john doe


Video Answer


1 Answers

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.

like image 140
Rob Napier Avatar answered Feb 11 '23 16:02

Rob Napier