Directly accessing nested dictionary values in Objective-C
Asked Answered
B

3

25

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?

Bloater answered 30/11, 2010 at 19:39 Comment(1)
Incidentally - in some languages (e.g. Javascript) these key/value data structures are called arrays, but in Objective-C terminology they're called dictionaries (specifically, NSDictionary objects). If you re-phrase your question it will help others to find it (and the answer) more easily. I'd suggest a title of "Directly accessing nested NSDictionary values", for example. Thanks!Knowland
K
63

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.

Knowland answered 30/11, 2010 at 19:50 Comment(1)
In case anyone looking for working link of Apple's Key-Value Coding Programming Guide developer.apple.com/library/archive/documentation/Cocoa/…Paradox
I
1

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"] );
Inflammable answered 16/11, 2013 at 6:4 Comment(2)
Thank you, Basil. Question. How are you able to NSLog without doing NSLog(@"%@", stuff); ??Susurrate
When calling NSLog(object) , the [object description] string is automatically called behind the scene.Cristobal
Y
0
gfbPicture = [[[result objectForKey:@"metadata"] objectForKey:@"pictures"] objectForKey:@"picture"];
Yachting answered 30/11, 2010 at 19:42 Comment(1)
No. use valueForKeyPath. (see Simon's answer above)Sesqui

© 2022 - 2024 — McMap. All rights reserved.