Data modeling: Dictionary with class as <value>

Hello, I have following questions regarding best data structure, it’s use and some interesting compiler answers:

first my ideas: I want to have readable code and want to access my data by using a String as index / key-value instead of using an array with 2 elements forced using a numeric index. Eg.:

count[“home”].balls instead of count[0].balls or
count["guest].strikes instead of count[1].strikes

1.) definition of a class

class PitchCount: ObservableObject {
var balls: Int
var strikes: Int

var totalPitchCount: Int {
    return balls + strikes
}

init(balls: Int, strikes: Int) {
    self.balls = balls
    self.strikes = strikes
}

}

2.) us of the class in a data model. I have deleted all the other properties due to better code reading

class TestModel: ObservableObject {

@Published var pitchCounts: [String: PitchCount] = ["guest": PitchCount(balls: 0, strikes: 0),
                                                   "home": PitchCount(balls: 0, strikes: 0)]

func changePitchCount(offensiveTeam: String, pitch: String) -> String{    
    if pitch == "ball" {
        pitchCounts[offensiveTeam]!.balls += 1
    } else {
        pitchCounts[offensiveTeam]!.strikes += 1
   }

}

Now my question. if I code pitchCounts[offensiveTeam].balls +=1 (no “!”), Xcode shows the following error message: “Value of optional type ‘Int?’ must be unwrapped to a value of type ‘Int’”. Force-unwrap using ‘!’ to abort execution if the optional value contains ‘nil’. Why? ‘balls’ is not declared as an optional.

What’s best practice for such a data use? Is Dictionary the right type for such an intended use?

Thanks and best regards

Peter

Now my question. if I code pitchCounts[offensiveTeam].balls +=1 (no “!”), Xcode shows the following error message: “Value of optional type ‘Int?’ must be unwrapped to a value of type ‘Int’”. Force-unwrap using ‘!’ to abort execution if the optional value contains ‘nil’. Why? ‘balls’ is not declared as an optional.

Accessing a Dictionary value by a key returns an Optional because the key may not exist in the Dictionary. It’s fine if you use pitchCounts["guest"] because there is an entry with that key, but what if you were to try pitchCount["kermit"]?

You can avoid the return of an Optional by supplying a default value to use if the key isn’t there:

let dict = ["item 1": 14, "item 2": 27]

let itemCount1 = dict["item 3"] 
// itemCount1 type will be Int? and value will be nil

let itemCount2 = dict["item 3", default: 0] 
// itemCount2 type will be Int and value will be 0

Is Dictionary the right type for such an intended use?

I don’t see that you need a Dictionary here. A game only involves two teams, right? So why not do something like this:

class TestModel: ObservableObject {
    @Published var guestPitches = PitchCount(balls: 0, strikes: 0)
    @Published var homePitches = PitchCount(balls: 0, strikes: 0)
}

And you wouldn’t even need the changePitchCount function because you could just do this:

model.guestPitches.balls += 1

Also, PitchCount doesn’t need to be an ObservableObject (you’re not observing anything!) and can therefore be made a struct instead of a class. You can then also remove the init since you can just use the default memberwise initializer that Swift gives you.

As an example:

import SwiftUI

struct PitchCount {
    var balls: Int
    var strikes: Int
    
    var totalPitches: Int {
        balls + strikes
    }
}

class TestModel: ObservableObject {
    @Published var guestPitches = PitchCount(balls: 0, strikes: 0)
    @Published var homePitches = PitchCount(balls: 0, strikes: 0)
}

struct BaseballView: View {
    @StateObject var model = TestModel()
    
    var body: some View {
        VStack(spacing: 40) {
            VStack(spacing: 20) {
                Text("Guest: \(model.guestPitches.balls) balls, \(model.guestPitches.strikes) strikes")
                Button("+ Ball") {
                    model.guestPitches.balls += 1
                }
                Button("+ Strike") {
                    model.guestPitches.strikes += 1
                }
            }

            VStack(spacing: 20) {
                Text("Home: \(model.homePitches.balls) balls, \(model.homePitches.strikes) strikes")
                Button("+ Ball") {
                    model.homePitches.balls += 1
                }
                Button("+ Strike") {
                    model.homePitches.strikes += 1
                }
            }
        }
    }
}

struct BaseballView_Previews: PreviewProvider {
    static var previews: some View {
        BaseballView()
    }
}
1 Like