Ivan Cantarino
Ivan Cantarino

Reputation: 3246

Dictionary not appending values

I think I'm introducing some logic error and I might be missing something here.

Please consider the following code:

    // Model
class MyModel: NSObject {
    let month: Int
    let destiny: String

    init(month: Int, destiny: String) {
        self.month = month
        self.destiny = destiny
    }
}

var datasource: [MyModel] = []
var dict: [Int : [MyModel]] = [:]

func fillDatasource() {
    for _ in 0...20 {
        let month = Int.random(in: 1...12)
        let destiny = "Any"
        let model = MyModel(month: month, destiny: destiny)
        datasource.append(model)
    }
}


func fillDict() {
    datasource.forEach {
        let month = $0.month        
        dict[month]?.append($0)
    }

    print(dict) // always empty
}

fillDatasource()
fillDict()

Inside my fillDict function the array is always nil. I think this is because the key doesn't exist , so the value cannot be appended to that specific key.

My question is: if the key doesn't exist, calling the append function would insert the key as well?

Am I missing something here?

Thanks.

Upvotes: 0

Views: 93

Answers (3)

Matic Oblak
Matic Oblak

Reputation: 16774

Your assumption is incorrect and there is no reason to think that this would insert a new array.

It might seem intuitive for this case but it may be very wrong for some cases. How about something like this:

garages[myName]?.parkCar(myCar)

Should this construct a new garage for my car? I think not. But even if so; what if default constructor is unavailable and this is actually defined as a protocol:

protocol Garage {
    func parkCar(_ car: Car)
}
var garages[String: Garage]

there is no way for Swift to fill in this object automatically.

Technically there would be a possible solution for this work that Swift would automatically construct an object for you in dictionary if this object had a default constructor and possibly the object type is a struct or a final class... But this would most likely only introduce more confusion than it would solve.

The most straight forward solution to your example is what @Sh_Khan wrote (but later deleted) which is:

if dict[month] == nil {
    dict[month] = [$0]
} 
else {
    dict[month]?.append($0)
}

Probably some more feasible approach would be

dict[month] = (dict[month] ?? []) + [$0]

but as described in a comment there is already a method that does exactly that for you:

dict[month, default: []].append($0)

I hope we can agree that this is a more general approach and it fixes all cases. For instance

garages[myName, default: PublicGarage(parkingSpot: myName)].parkCar(myCar)

Upvotes: 3

AbdelAli
AbdelAli

Reputation: 816

Dic is empty because dic[month] is nil, the value has never been altered.

To group array by a property of the array elem, I'd use the following:

dic = Dictionary(grouping: datasource) { (model) -> Int in
    return model.month
}

Upvotes: 0

Muhammad Umar
Muhammad Umar

Reputation: 11

You can update your fillDict method to the following:

func fillDict() {
    datasource.forEach {
        let month = $0.month
        
        if dict.keys.contains(month) {
            dict[month]?.append($0)
        } else {
            dict[month] = [$0]
        }
    }

    print(dict)
}

Explanation: We need to check if the month key already exits in the dictionary, than append in it's array else we are assigning a new array against a month in the dictionary

Upvotes: 1

Related Questions