Deepak
Deepak

Reputation: 436

Traverse nsdictionary in swift

I am new to swift. I have my dictionary as

monthData = 
{
    "2018-08-10" = {
        accuracy = 71;
        attempted = 7;
        correct = 5;
        reward = Bronze;
    };
    "2018-08-12" = {
        accuracy = 13;
        attempted = 15;
        correct = 2;
        reward = "";
    };
    "2018-08-13" = {
        accuracy = 33;
        attempted = 15;
        correct = 5;
        reward = "";
    };
    "2018-08-14" = {
        accuracy = 100;
        attempted = 15;
        correct = 15;
        reward = Gold;
    };
    "2018-08-16" = {
        accuracy = 73;
        attempted = 15;
        correct = 11;
        reward = Silver;
    };
    "2018-08-21" = {
        accuracy = 26;
        attempted = 15;
        correct = 4;
        reward = "";
    };
    "2018-08-23" = {
        accuracy = 46;
        attempted = 15;
        correct = 7;
        reward = "";
    };
}

I want to get all the dates for which reward is Gold

Can anyone please help me do that?

What I have tried 'till now is:

for (key,value) in monthData{
   let temp = monthData.value(forKey: key as! String) as! NSDictionary
   for (key1,value1) in temp{
     if((value1 as! String) == "Gold"){
       print("keyFINAL \(key)")
     }
}

but it outputs the error Could not cast value of type '__NSCFNumber' to 'NSString'

Upvotes: 1

Views: 185

Answers (2)

Bhavin Kansagara
Bhavin Kansagara

Reputation: 2916

From the first for in loop, you are getting the NSDictionary in temp variable

"2018-08-16" =     {
    accuracy = 73;
    attempted = 15;
    correct = 11;
    reward = Silver;
};

So, you should directly check .value(forKey:) on temp and get the value for reward.

You should try it like this

for (key,value) in monthData {
        let temp = monthData.value(forKey: key as! String) as! NSDictionary
        if(((temp.value(forKey: "reward")) as! String) == "Gold"){
            print("keyFINAL \(key)")
        }
    }

Try and share results

EDIT

Please checkout the answer from vadian for in-depth explanation and pure swift approach to achieve the same.

Thanks

Upvotes: 0

vadian
vadian

Reputation: 285270

The error occurs because when you are iterating the dictionary you force cast the Int values to String which is not possible

The (highly) recommended Swift way is to use the filter function. This is much more efficient than a loop.

In the closure $0.1 represents the value of the current dictionary ($0.0 would be the key). The result is an array of the date strings.

let data : [String:Any] = ["monthData" : ["2018-08-10": ["accuracy" : 71, "attempted" ... ]]]

if let monthData = data["monthData"] as? [String:[String:Any]] {
    let goldData = monthData.filter { $0.1["reward"] as? String == "Gold" }
    let allDates = Array(goldData.keys)
    print(allDates)
}

The code safely unwraps all optionals.

However if there is only one Gold entry the first function is still more efficient than filter

if let monthData = data["monthData"] as? [String:[String : Any]] {
    if let goldData = monthData.first( where: {$0.1["reward"] as? String == "Gold" }) {
       let goldDate = goldData.key
        print(goldDate)
    }
}

In Swift avoid the ObjC runtime (value(forKey:)) and Foundation collection types (NSDictionary) as much as possible.

Upvotes: 1

Related Questions