fuzzygoat
fuzzygoat

Reputation: 26223

Access NSDictionary via dot notation?

Is there a way via dot notation to access the values of keys in an NSDictionary like this?

NSDictionary *returnVal = [NSDictionary dictionaryWithObjectsAndKeys:@"Saturn", @"name", @"Gas Giant", @"type", nil];
NSLog(@"VALUE: %@", [returnVal valueForKey:@"name"]); // This is how I am doing it now.

Upvotes: 7

Views: 5213

Answers (9)

eharo2
eharo2

Reputation: 2642

In Swift, there is a solution that may not seem very elegant but does the trick.

It will require a typeAlias for each specific type of Dictionary and also an extension with variables (with getter/setter) for each of the expected keys in your dictionary. Not a good practice at all

It may be easier wrap your dict object in an object (class/struct) with the same treatment.

typealias MyDict = [String:AnyObject]
extension MyDict {
    var key: AnyObject? {
        get { return self["key"] }
        set { self["key"] = newValue }
    }
}


// Usage
var myDict = MyDict()

// Get the value
myDict["key"] = "value1" as AnyObject
if let str = myDict.key {
    print(str)  // prints "value1"
}

// Set the value
myDict.key = "value2" as AnyObject
if let str = myDict["key"] {
    print(str)  // prints "value2"
}

Upvotes: 0

Al.
Al.

Reputation: 301

The answer's still no, but you can use the shorthand

myDictionary[@"key"]

instead of

[myDictionary objectForKey:@"key"]

Upvotes: 0

KlausCPH
KlausCPH

Reputation: 1835

I agree with most of the answers that NSDictionary should be accessed with objectForKey: or similar methods. However it is possible to allow for dot notation access to a NSDictionary, and for learning purposes this might be interesting for someone. Also when for example your are retrieving large JSON dictionaries via AFNetworking, this method can ease the access and readability of your code.

This is my solution:

DictionaryProperties.h: (class wrapping the NSDictionary for property access)

@interface DictionaryProperties : NSObject{
    NSMutableDictionary* _backingDict;
}

@property (nonatomic, strong) NSMutableDictionary* backingDict;

+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict;

@end

DictionaryProperties.m:

#import "DictionaryProperties.h"

@implementation DictionaryProperties

@synthesize backingDict = _backingDict;

- (id) initWithDictionary:(NSDictionary*)dict {
    if (self) {
        if ([dict isKindOfClass:[NSMutableDictionary class]]) {
            self.backingDict = (id)dict;
        } else {
            self.backingDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
        }
    }
    return self;
}
+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict {
    return [[DictionaryProperties alloc] initWithDictionary:dict];
}


- (void)forwardInvocation:(NSInvocation *)invocation
{
    NSString* key = NSStringFromSelector(invocation.selector);
    invocation.selector = @selector(objectForKey:);
    [invocation setArgument:&key atIndex:2];

    if ([self.backingDict objectForKey:key]) {
        [invocation invokeWithTarget:self.backingDict];
    } else {
        [self doesNotRecognizeSelector:invocation.selector];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return [self.backingDict methodSignatureForSelector:@selector(objectForKey:)];

}
@end

ExampleDictContent.h: (class declaring what is inside the dictionary)

#import "DictionaryProperties.h"

@interface ExampleDictContent : DictionaryProperties

@property (strong, nonatomic) NSString* someData;
@property (strong, nonatomic) NSString* someOtherData;

@end

@implementation ExampleDictContent
@end

Usage: (simple declaration of a dictionary, allocation of wrapper and property access)

#import "ExampleDictContent.h"

NSDictionary* d = [NSDictionary dictionaryWithObjects:NSArray arrayWithObjects:@"someData content", @"someOtherData content", nil
                                              forKeys:NSArray arrayWithObjects:@"someData", @"someOtherData", nil];

ExampleDictContent* dictWProps = [ExampleDictContent allocWithDictionary:d];

NSLog(dictWProps.someData);
NSLog(dictWProps.someData);

This will print:

someData content
someOtherData content

So basically DictionaryProperties works as a facade for accessing the NSDictionary. It uses forwardInvocation to convert a get-property method call into a getObjectForKey: call on the dictionary. What I like about it, is that it allows for autocompletion on the dictionary, and also allows me to explicitly declare what keys I want to access (in the ExampleDictContent.h file). Note that this solution does not allow for write access to the properties, but that can be added as shown in the link below.

This solution has partly been inspired by karstenlitsche's solution. The main difference is that this solution is based on sub-classing instead of categories.

Upvotes: 3

kevboh
kevboh

Reputation: 5245

There is no dot syntax for NSDictionary, but should consider using objectForKey: instead of valueForKey:

Difference between objectForKey and valueForKey?

Upvotes: 6

benzado
benzado

Reputation: 84338

Not really, no.

The dot notation is a shorthand way of calling a method with that selector name. In other words, this...

NSLog(@"Hello, %@", foo.bar.name);

...is the same as this...

NSLog(@"Hello, %@", [[foo bar] name]);

When I say "same", I mean they are compiled down to the same code. It's just syntactic sugar.

A plain NSDictionary won't act that way. You could sort of fake it with Key Value Coding, which lets you call valueForKeyPath to get properties like this:

NSLog(@"Hello, %@", [foo valueForKeyPath:@"bar.name"]);

If you really wanted to be able to write foo.bar.name in your code, however, you'd have to make a custom class that overrides forwardInvocation:; this lets you catch an unknown message to an object and do something else with it besides throw an error. In this case, you could change the unknown selector to a lookup on an NSDictionary instance it contains.

But even if you did that, the compiler would probably still generate warnings unless you made header files that declared those property names to exist.

Upvotes: 5

Richard J. Ross III
Richard J. Ross III

Reputation: 55573

Technically, you can do something like this:

typedef id (^valueBlock)(id);
@interface NSDictionary(dotNotationAddons)

@property(nonatomic, readonly) valueBlock value;

@end

@implementation NSDictionary(dotNotationAddons)

-(valueBlock) value
{
    return [[^(id key) {
        return [self objectForKey:key];  
    } copy] autorelease];
}

@end

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"One", @"2", @"Two", @"3", @"Three", @"4", @"Four", nil];

        id value = dictionary.value(@"One");

        NSLog(@"%@", value);
    }


    return 0;
}

I don't know if that is what you were looking for, but I hope it helps!

Upvotes: 0

bensnider
bensnider

Reputation: 3772

No, you are doing it the correct way. In the iOS world, often the correct way is the only way. :)

If you really want dot notation (and other nice things you get with typed objects), you're going to have to stuff the dictionary representation into an object. Most commonly my interface will look like:

@interface FooBar : NSObject {
    NSString *someData;
    int someNumber;
}

@property (nonatomic, copy) NSString *someData;
@property (nonatomic, assign) int someNumber;

+ (FooBar *)FooBarFromDictionary:(NSDictionary *)dataDict;

@end

The implementation should be clear. Then you can

FooBar *fb = [FooBar FooBarFromDictionary:data];
NSLog(@"fb.someData = %@", fb.someData);

Upvotes: 1

rishi
rishi

Reputation: 11839

The way that you have mentioned for accessing element of dictionary is ideal way(using keys). If you want to do something else, might be you can use-

NSArray *allValues = [returnVal allValues];

Now using this array as well you can perform tasks. And if you want something specific then mention that, might be for that there can be some other way.

Also as NSDictionary class won't have any property defined, so dot notation is directly not possible.

Upvotes: 1

El Developer
El Developer

Reputation: 3346

No, I don't think so.

From the reference manual.

Accessing Keys and Values
– allKeys
– allKeysForObject:
– allValues
– getObjects:andKeys:
– objectForKey:
– objectsForKeys:notFoundMarker:
– valueForKey:

That's listed as the only way to access the keys and the values. So you are doing it alright.

You would be able to access it if the keys were a public property and it was readable.

Upvotes: 1

Related Questions