Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directly accessing nested dictionary values in Objective-C

Tags:

Is there a way to directly access an inner-array of an an outer array in Objective-C? For example, a call to an external data source returns the following object:

{
bio = "this is the profile.bio data";
"first_name" = John;
"last_name" = Doe;
location =     {
    name = "Any Town, Any State";
};
metadata =    {
    pictures =    {
        picture = "https://picture.mysite.com/picture.jpeg";
    }
}
}

I want to be able to access, for example, the location.name or the metadata.pictures.picture data. Dot notation, however, does not seem to work. For example:

_gfbLocation = [result objectForKey:@"location.name"];
_gfbPicture = [result objectForKey:@"metadata.pictures.picture"];

The only way I have been able to access this data is by first setting the contents of the inner arrays to objects. Thoughts?

like image 276
user455889 Avatar asked Nov 30 '10 19:11

user455889


People also ask

How do I access nested dict values?

Access Nested Dictionary Items You can access individual items in a nested dictionary by specifying key in multiple square brackets. If you refer to a key that is not in the nested dictionary, an exception is raised. To avoid such exception, you can use the special dictionary get() method.

How do I add an array to a dictionary in Objective C?

Create an empty NSMutableDictionary called 'shoeOrderDict'. NSMutableDictionary *shoeOrderDict = [[NSMutableDictionary alloc] init]; Now add the values in our array to our new dictionary. The keys for the values should be: 'customer', 'size', 'style' and 'color'.

What is dictionary Objective C?

Objective-C Quick Syntax Reference by NSDictionary is a class used to organize objects in lists using keys and values. NSDictionary can maintain an index of objects and let you retrieve an object if you have the right key.

Can dictionaries be nested to any depth?

Dictionaries can be nested to any depth. A dictionary can contain any object type except another dictionary. All the keys in a dictionary must be of the same type.


2 Answers

For nested keys like that you can use a keyPath. A keyPath is just a series of keys joined with dots. You can use them to retrieve nested values from objects that support Key-Value Coding - including NSDictionary objects like yours. So in your case this should work:

[result valueForKeyPath:@"location.name"];

For more detail on Key-Value Coding, see Apple's Key-Value Coding Programming Guide.

See also this related StackOverflow question.

like image 195
Simon Whitaker Avatar answered Nov 06 '22 06:11

Simon Whitaker


Using the correct answer by Simon Whitaker, I was able to build a hierarchy of constants by embedding a Dictionary in a Dictionary in a Dictionary. Below is example source code, modified from my real source code.

This is a real-world problem-solution. In my particular case, the goal was organizing the strings that identify products accessed via StoreKit for In-App Purchase in Apple's App Store for iOS. Imagine our app presents content from a pair of books, one on cats, the other dogs. Furthermore, our app sells an abridged version of the content as well as unabridged. Upgrading from the abridged to the unabridged means a third product, "upgrade". Each pair of books might be translated, in this case English and Italian.

Looking at the strings I'm trying to track, you might think "Why doesn't that guy just use the strings themselves rather than going through this KVC nonsense?". Well, notice the 2nd string, English > Cats > Unabridged. The string ends with an appended underscore. That's because when I used iTunesConnect to create the In-App Purchase products, I accidentally created that item as "Consumable" instead of "Non-Consumable". Apple does not allow changing the ID, even if you delete said product. So the original string could not be used; alternatively, I appended the underscore as a workaround. So the point is, these strings are arbitrary and messy.

Another similar need for this approach would by if these string values might occasionally change at compile-time, so you don't want to be copy-pasting into more than one place in your source-code. A hierarchy of constants, in other words.

Inside Xcode, I want a better way of referring to these product identifiers.

// Using new literals syntax in later versions of Xcode 4 (& 5) to declare and populate a dictionary nested in a dictionary also in a dictionary.
NSDictionary *productIdentifiersHierarchy = @{
                                              @"en" : @{
                                                      @"cats" : @{
                                                              @"abridged" : @"com.example.My_App.cats_abridged_en",
                                                              @"unabridged" : @"com.example.My_App.cats_unabridged_en_",
                                                              @"upgrade" : @"com.example.My_App.cats_upgrade_en"
                                                              },
                                                      @"dogs" :  @{
                                                              @"abridged" : @"com.example.My_App.dogs_abridged_en",
                                                              @"unabridged" : @"com.example.My_App.dogs_unabridged_en",
                                                              @"upgrade" : @"com.example.My_App.dogs_upgrade_en"
                                                              }
                                                      },
                                              @"it" : @{
                                                      @"cats" : @{
                                                              @"abridged" : @"com.example.My_App.cats_abridged_it",
                                                              @"unabridged" : @"com.example.My_App.cats_unabridged_it",
                                                              @"upgrade" : @"com.example.My_App.cats_upgrade_it"
                                                              },
                                                      @"dogs" :  @{
                                                              @"abridged" : @"com.example.My_App.dogs_abridged_it",
                                                              @"unabridged" : @"com.example.My_App.dogs_unabridged_it",
                                                              @"upgrade" : @"com.example.My_App.dogs_upgrade_it"
                                                              }
                                                      }
                                              };

Here's how to access these triple-nested dictionaries.

// Use KVC (Key-Value Coding) as a convenient way to access the nested dictionary structure.
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.cats.abridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.cats.unabridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.cats.upgrade"],

NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.dogs.abridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.dogs.unabridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"en.dogs.upgrade"],

NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.cats.abridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.cats.unabridged"],
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.cats.upgrade"],

NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.dogs.abridged"] );
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.dogs.unabridged"] );
NSLog( [productIdentifiersHierarchy valueForKeyPath:@"it.dogs.upgrade"] );
like image 20
Basil Bourque Avatar answered Nov 06 '22 07:11

Basil Bourque