Using keypaths to add a value to a variable?

I have had a similar problem previously and someone suggested keypaths might be the answer

I want to add to the number of blueBandages

Code Block
struct Product {
    var brand: String
    var option1: String
    var option2: String
    var item1: String
    var item2: String
}
//inventory
var blueBandages = 0
var redBandages = 0
var yellowBandages = 0
var greenBandages = 0
//catalog
let products = [
    Product(brand: "Academia", option1: "Blue", option2: "Red", item1: "blueBandages", item2: "redBandages"),
    Product(brand: "Academia", option1: "Yellow", option2: "Green", item1: "yellowBandages", item2: "greenBandages")]
func buy() {
//???
}


The obvious way is the following:
Code Block
func buy() {
blueBandages += 1
}

But this will require a lengthy If statement to identify which item to add. I will have to add to the if statement every time I add an item to the catalog
Code Block
var selectedProduct = 0
if selectedProduct == "Academia blueBandages":
blueBandages += 1

I wish I could "unwrap" some string as executable code, but I don't think this exists.
Code Block
func buy() {
Code(product[1].item1) =+ 1
}
//if only this worked the same as the previous code chunk

Maybe keypaths are the answer? I haven't been able to make it work, because my key path is still acting like a string and not executable code

Answered by OOPer in 657583022

 I would have to put in an If or Switch statement to input the correct ending, but this can get messy.  

If you want to choose the item with an integer value, you should better use Array:
Code Block
struct Product {
var brand: String
var option1: String
var option2: String
var items: [String]
}
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0,
"greenBandages": 0
]
//catalog
let products = [
Product(brand: "Academia", option1: "Blue", option2: "Red", items: ["blueBandages", "redBandages"/*,...*/]),
Product(brand: "Academia", option1: "Yellow", option2: "Green", items: ["yellowBandages", "greenBandages"/*,...*/]),
]


Code Block
@IBAction func buyButton(_ sender: UIButton) {
let chosenItem = sender.tag
let inventoryKey = products[productIndex].items[chosenItem]
//...
inventory[inventoryKey, default: 0] += 1
}


Maybe keypaths are the answer?

That can be one way, there may be others.
Why don't you simply use Dictionary?
Code Block
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0
"greenBandages": 0
]


I haven't been able to make it work, because my key path is still acting like a string and not executable code

Please show the code.
Using a dictionary seems like a good idea, how can I change a value in the dictionary when updating amounts?

Code Block import UIKit
struct Product {
    var brand: String
    var option1: String
    var option2: String
    var item1: String
    var item2: String
}
var inventory: [String: Int] = [
    "blueBandages": 0,
    "redBandages": 0,
    "yellowBandages": 0,
    "greenBandages": 0
]
//catalog
let products = [
    Product(brand: "Academia", option1: "Blue", option2: "Red", item1: "blueBandages", item2: "redBandages"),
    Product(brand: "Academia", option1: "Yellow", option2: "Green", item1: "yellowBandages", item2: "greenBandages")]
let brandKeyPath = \Product.brand
print(products[1][keyPath: brandKeyPath])
print(brandKeyPath)
func buy() {
    brandKeyPath += 1
}




You have
selectedProduct = "Academia blueBandages"

What is really selected ?
  • a product (with all properties: brand, option1…)

  • or an item in inventory ?

In Product, some information seems redundant : the option (color) and the item (bandage)
  • Why do you need both ?

  • Why don't you have an array for items ?

Code Block
struct Product {
var brand: String
var option1: String // is it really needed ? You can infer from the item.
var option2: String
var items: [String]
}

What do you want to do ?
  • user select a Product

=> then you know which items are selected
  • what do you want to do in inventory ? remove 1 item ? Why do you add ?

Anyway, with a dictionary as
Code Block
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0, // Need comma here
"greenBandages": 0
]

Then
Code Block
let product1 = Product(brand: "Academia", option1: "Blue", option2: "Red", items: ["blueBandages", "redBandages"])
let product2 = Product(brand: "Academia", option1: "Yellow", option2: "Green", items: ["yellowBandages", "greenBandages"])
let products = [product1, product2]
func buy(product: Product) {
for item in product.items {
inventory[item]? += 1
}
}
buy(product: products[0])
print(inventory)
buy(product: products[1])
print(inventory)


gives
["greenBandages": 0, "redBandages": 1, "blueBandages": 1, "yellowBandages": 0]

["greenBandages": 1, "redBandages": 1, "blueBandages": 1, "yellowBandages": 1]
To derive color from items:

Code Block
let bandColors: [String: String] = [
"blueBandages": "Blue",
"redBandages": "Red",
"yellowBandages": "Yellow",
"greenBandages": "Green"
]
struct Product {
var brand: String
var items: [String]
var colors: [String] {
items.map { bandColors[$0] ?? "None"}
}
}
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0,
"greenBandages": 0
]
let product1 = Product(brand: "Academia", items: ["blueBandages", "redBandages"])
let product2 = Product(brand: "Academia", items: ["yellowBandages", "greenBandages"])
let products = [product1, product2]
func buy(product: Product) {
for item in product.items {
inventory[item]? += 1
}
}
print(products[0].colors)
buy(product: products[0])
print(inventory)
buy(product: products[1])
print(inventory)


Which gives:
["Blue", "Red"]
["yellowBandages": 0, "redBandages": 1, "greenBandages": 0, "blueBandages": 1]
["yellowBandages": 1, "redBandages": 1, "greenBandages": 1, "blueBandages": 1]

how can I change a value in the dictionary when updating amounts?

Like this:
Code Block
struct Product {
var brand: String
var option1: String
var option2: String
var item1: String
var item2: String
}
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0,
"greenBandages": 0
]
//catalog
let products = [
Product(brand: "Academia", option1: "Blue", option2: "Red", item1: "blueBandages", item2: "redBandages"),
Product(brand: "Academia", option1: "Yellow", option2: "Green", item1: "yellowBandages", item2: "greenBandages"),
]
let inventoryKey = products[1].item2
func buy() {
inventory[inventoryKey, default: 0] += 1
}


Ok, I have this now:

productIndex created from the indexPath from the collectionView that contains the products in a previous VC. The color options for each items appear as buttons along the bottom (chosenItem)

Code Block
var productIndex = 0
    @IBAction func buyButton(_ sender: UIButton) {
        var chosenItem = sender.tag
        let inventoryKey = products[productIndex].chosenItem
      
        func buyButton() {
            inventory[inventoryKey, default: 0] += 1
        }

however, I am not able to find the color choice (item1, item2) by putting in the var at the end of line 4. It tries to find the property chosenItem that does not exist. I would have to put in an If or Switch statement to input the correct ending, but this can get messy.

Code Block  @IBAction func buyButton(_ sender: UIButton) {
       
        var inventoryKey = products[productIndex].item1
        switch sender.tag {
        case 0:
            inventoryKey = products[productIndex].item1
        case 1:
            inventoryKey = products[productIndex].item2
        case 2:
            inventoryKey = products[productIndex].item3
        case 3:
            inventoryKey = products[productIndex].item4
        default:
            inventoryKey = products[productIndex].item1
        }
            inventory[inventoryKey, default: 0] += 1
            print(inventory["academiaBandagesBlue"]!)
    


Accepted Answer

 I would have to put in an If or Switch statement to input the correct ending, but this can get messy.  

If you want to choose the item with an integer value, you should better use Array:
Code Block
struct Product {
var brand: String
var option1: String
var option2: String
var items: [String]
}
var inventory: [String: Int] = [
"blueBandages": 0,
"redBandages": 0,
"yellowBandages": 0,
"greenBandages": 0
]
//catalog
let products = [
Product(brand: "Academia", option1: "Blue", option2: "Red", items: ["blueBandages", "redBandages"/*,...*/]),
Product(brand: "Academia", option1: "Yellow", option2: "Green", items: ["yellowBandages", "greenBandages"/*,...*/]),
]


Code Block
@IBAction func buyButton(_ sender: UIButton) {
let chosenItem = sender.tag
let inventoryKey = products[productIndex].items[chosenItem]
//...
inventory[inventoryKey, default: 0] += 1
}


Using an array within the struct has helped a lot! I'll be editing a lot of my app if I can make this work. I'm having trouble changing the values in the dictionary though.

Code Block
        print(productIndex)
        let inventoryKey = products[productIndex].item[chosenItem]
        print(inventoryKey)
        inventory[inventoryKey, default: 0] += 1
    print(inventoryKey)

When I click a different product, the productIndex (productIndex = indexPath.row from previous VC) is correct, but the inventoryKey is always from productIndex = 0 regardless of what productIndex is clicked. It appears it is using the default value for productIndex; chosenItem is working correctly
Why didn't you try with a plain dictionary, not using key path ? Doesn't it work ?
It's working now! I had a small typo :) Thanks for the help!

Code Block
import UIKit
struct Product {
    var brand: String
    var option: [String]
    var item: [String]
}
var inventory: [String: Int] = [
    "blueBandages": 0,
    "redBandages": 0,
    "yellowBandages": 0,
    "greenBandages": 0
]
//catalog
let products = [
    Product(brand: "Academia", option: ["Blue", "Red"], item: ["blueBandages", "redBandages"]),
    Product(brand: "Academia", option: ["Yellow", "Green"], item: ["yellowBandages", "greenBandages"])
]
let productIndex = 1
let chosenItem = 1
let inventoryKey = products[productIndex].item[chosenItem]
    print(products[productIndex].item[chosenItem])
    print(inventory[inventoryKey, default: 0])
 
    inventory[inventoryKey, default: 0] += 1
    print(products[productIndex].item[chosenItem])
    print(inventory[inventoryKey, default: 0])


Using keypaths to add a value to a variable?
 
 
Q