NSMutableDictionary keyEnumerator or NSArray? - objective-c

I have an NSMutableDictionary with a structure like:
Main Dictionary > Unknown Dictionary > Dictionaries 1,2,4,5,6...
My question is what is the best way to retrieve the Unknown Dictionary key and set it as a variable? This is what I've tried:
NSEnumerator *enumerator = [myMutableDict keyEnumerator];
id aKey = nil;
while ( (aKey = [enumerator nextObject]) != nil) {
id value = [myMutableDict objectForKey:aKey]; // changed to `aKey`
NSLog(#"%#: %#", aKey, value); // tip via rmaddy
}
What goes into objectForKey: if you don't know the name of the object in the key?
The other thought I had was to populate an NSArray, then pulling each of the keys out somehow.
for (NSString *object in myMutableDict)
myArray = [myArray arrayByAddingObject:MainDict];
}
If anyone can suggest a better way to get the object (unknown) from an NSMutableDictionary I'm interested to learn.

You can enumerate dictionaries like this:
NSDictionary * someDictionary = ... however you set your dictionary;
[someDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(#"Key: %#", key);
NSLog(#"Object: %#", obj);
}];
and set:
*stop = YES;
when you find the object you're looking for.

I'm not entirely sure if I understand your question correctly. I assume you have a "main" dictionary with exactly one (unknown) key that maps to another dictionary, which you want to retrieve. This would be a simple and concise way to do this:
NSDictionary *unknownDictionary = mainDictionary[mainDictionary.allKeys.firstObject];
(Yes, some people won't like the dot syntax here, but I find it easier to read in this case. You might also want to add some error checking, for the case that mainDictionary is empty etc.)

Related

Search String in NSDictionary store in NSMutableArray

I am trying to search a String in NSDictionary stored in NSMutableArray
for (int k = 0; k < [onlyActiveArr count]; k++) {
NSString *localID = [onlyActiveArr objectAtIndex:k];
NSLog(#"%#",localID);
int localIndex = [onlyActiveArr indexOfObject:localActiveCallID];
NSLog(#"%d",localIndex);
for (NSDictionary *dict in self.SummaryArr) {
NSLog(#"%#",[dict objectForKey:#"ActiveID"]);
if (![[dict objectForKey:#"ActiveID"] isEqualToString:localID]) {
NSLog(#"found such a key, e.g. %#",localID);
}
}
}
But I am getting
NSLog(#"found such a key, e.g. %#",localActiveCallID);
when the ID is still there in SummaryArr, I am checking if localID retrieved from onlyActiveArr is not present in dictionary.
Please suggest me how to overcome my problem.
You cannot make a decision that a key is not present until you finish processing the entire dictionary. Make a boolean variable initially set to NO, and change it to YES if you find an item in the dictionary, like this:
BOOL found = NO;
for (NSDictionary *dict in self.SummaryArr) {
NSLog(#"%#",[dict objectForKey:#"ActiveID"]);
found = [[dict objectForKey:#"ActiveID"] isEqualToString:localID];
if (found) break;
}
if (!found) {
NSLog(#"found such a key, e.g. %#",localID);
}
If you like predicates, then you can use the fact that accessing an inexistent key in a dictionary produces a nil value and make a predicate that filters out those dictionaries that have nil for your key.
If the count of the result is larger than zero, your key is somewhere in the array. It won't tell you where, though.
A snippet to show the idea:
NSPredicate *keyPred = [NSPredicate predicateWithFormat: #"NOT ActiveID == nil"];
BOOL found = [[self.SummaryArr filteredArrayUsingPredicate: keyPred] count] > 0;
I don't know how the performance stacks up, so if your data set is large you may want to check that the execution time is within your limits.

Parsing a JSON of unknown Structure Objective-C

I am trying to parse this json and am not sure of parsing this because the keys like "vis-progress-control" might change and I am trying to build a general code which parses this type of a json. I am sure that "type" key will be present in the json structure.
NSDictionary *dict = [sData JSONValue];
NSArray *items = [dict valueForKeyPath:#"assets"];
NSLog(#"%#", items);
for (NSString *key in [[dict objectForKey:#"assets"]allKeys]) {
for (NSString *subKey in [[[dict objectForKey:#"assets"] objectForKey:key]allKeys]) {
NSLog(#"Value at subkey:%# is %#\n",subKey,[[[dict objectForKey:#"assets"]objectForKey:key]objectForKey:subKey]);
}
}
I am using the SBJson Library on Github. My actual issue is How do I access "direction", "degrees" etc when I do not know the "vjs-progress-holder" key?
I have also a widgets array nested within a widgets array. How do I get these values as well?
Read about tree traversal. Sounds like you want to traverse the "tree" of nodes and when you find a particular leaf value you will then traverse back up one level and know the parent is the container you want.
So the idea is that once it's parsed from the JSON, forget it's JSON because now it's just a tree in arrays and dictionaries. Traverse it by getting all the keys in the dictionary via allKeys (returns an array of keys) and then iterate through them getting the associated values (using something like (psuedo code):
for ( NSString * key in allkeysarray) {
NSString * val = [dict objectForKey: key];
if ( [val isEqualToString: #"gradient"] )
{
// now you know that this dictionary is the one you're looking for so you can maybe break out of this loop and just use the keys you know reference these values.
break;
}
}
hopefully that's enough to get you going.
Assuming I'm understanding your objective here, it seems like you want to do something like this?
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
float degrees = [asset[#"degrees"] floatValue];
// and read whatever other values you need for gradients
}
if ([type isEqualToString:#"image"]) {
// and read the appropriate values for images here...
}
}
So I'll make a different assumption here, which is that you want an array of gradients. So then that looks basically like this:
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
NSMutableArray *gradients = [NSMutableArray array];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
// Add this asset to the list of gradients:
[gradients addObject:asset];
}
if ([type isEqualToString:#"image"]) {
// do something similar for images
}
}
Then, after having done that, if you would like to read the "degrees" field for the 4th gradient, for example, you will find it at gradients[3][#"degrees"]

How to efficiently access large objects in Obj-C using objectForKey and objectAtIndex?

If I have a large NSDirectory typically from a parsed JSON-object I can access this object with code like so:
[[[[[obj objectForKey:#"root"] objectForKey:#"key1"] objectAtIndex:idx] objectForKey:#"key2"] objectAtIndex:idz];
The line might be a lot longer than this.
Can I optimize this code in any way? At least make it easier to read?
This line will also generate a runtime-error if the object does not correspond, what is the most efficient way to avoid that?
If you were using -objectForKey: for everything you could use -valueForKeyPath:, as in
[obj valueForKeyPath:#"key1.key2.key3.key4"]
However, this doesn't work when you need to use -objectAtIndex:. I don't think there's any good solution for you. -valueForKeyPath: also wouldn't solve the problem of the runtime errors.
If you truly want a simple way to do this you could write your own version of -valueForKeyPath: (call it something else) that provides a syntax for specifying an -objectAtIndex: instead of a key, and that does the appropriate dynamic checks to ensure the object actually responds to the method in question.
If you want easier to read code you can split the line into several lines like this
MyClass *rootObject = [obj objectForKey:#"root"];
MyClass *key1Object = [rootObject objectForKey:#"key1"];
MyClass *myObject = [key1Object objectAtIndex:idx];
...
and so forth.
I think, you can create some array, that will contain full "path" to your object. The only thing, you need to store your indexes somehow, maybe in NSNumber, in this case you cannot use NSNumber objects as keys in your dictionaries. Then create a method, that will return needed object for this given "path". smth like
NSMutableArray* basePath = [NSMutableArray arrayWithObjects: #"first", [NSNumber numberWithInt:index], nil];
id object = [self objectForPath:basePath inContainer:container];
- (id) objectForPath:(NSMutableArray*)basePath inContainer:(id)container
{
id result = nil;
id pathComponent = [basePath objectAtIndex: 0];
[basePath removeObjectAtIndex: 0];
// check if it is a number with int index
if( [pathComponent isKindOfClass:[NSNumber class]] )
{
result = [container objectAtIndex: [pathComponent intValue]];
}
else
{
result = [container objectForKey: pathComponent];
}
assert( result != nil );
// check if it is need to continue searching object
if( [basePath count] > 0 )
{
return [self objectForPath:basePath inContainer: result];
}
else
{
return result;
}
}
this is just an idea, but I hope you understand what I mean. And as Kevin mentioned above, if you don't have indexes, you can use key-value coding.
Don't know if it can suit you, but you could also give a try to blocks, I always find them very convenient. At least they made code much more readable.
NSArray *filter = [NSArray arrayWithObjects:#"pathToFind", #"pathToFind2",nil];
NSPredicate *filterBlock = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSArray *root = (NSArray*)obj;
// cycle the array and found what you need.
// eventually implementing some sort of exit strategy
}];
[rootObject filteredArrayUsingPredicate:filterBlock];

Ways to replace massive if statement with alternative construct in Objective-C

I have a fairly lengthy if statement. The if statement examines a string "type" to determine what type of object should be instantiated. Here's a sample...
if ( [type rangeOfString:#"coin-large"].location != NSNotFound )
{
... create large coin ...
mgr = gameLayer.coinLargeMgr;
}
else if ( [type rangeOfString:#"coin-small"].location != NSNotFound )
{
mgr = gameLayer.coinLargeMgr;
}
... more else statements ...
myObject = [mgr getNewObject];
The "else-if" statements continue for other object types which stand at about 20 right now and that number is likely to increase. This works quite well but in terms of maintenance and efficiency I think it could be improved. My leading candidate right now is to create an NSDictionary keyed on the object type string (coin-small, coin-large, etc.) and with the value of the manager object that should be tied to that type. The idea being that this would be a quick look for the type of object I need to create. Not sure this is the best approach, continuing to look at other options but am curious what folks here might have done for a similar problem. Any help/feedback is greatly appreciated.
You can use an NSDictionary filled with ObjC 'Blocks' to do a switch-like statement which executes the desired code. So make a dictionary with your string keys mapped to a block of code to execute when each is found:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
^{ NSLog(#"found key1"); }, #"key1",
^{ NSLog(#"found key2"); }, #"key2",
nil];
You'll probably prepare this dictionary only once at some early stage like in a constructor or a static initializer so that it is ready when your later code executes.
Then instead of your if/else block, slice out the string key from whatever intput you are receiving (or maybe you won't need to slice it, whatever):
NSString *input = ...
NSRange range = ...
NSString *key = [input substringWithRange:range];
And do the (fast) dictionary lookup for the code to execute. Then execute:
void (^myBlock)(void) = [dict objectForKey:key];
myBlock();
The dictionary approach would be easily doable. Assuming the various managers have been boiled down to specific instances when you create the dictionary, it'd be just the same as almost any object-oriented language:
NSDictionary *stringsToManagers =
[NSDictionary dictionaryWithObjectsAndKeys:
#"coin-large", gameLayer.coinLargeMgr,
#"coin-small", gameLayer.coinSmallMgr,
nil];
// this is assuming that type may contain multiple types; otherwise
// just use [stringsToManagers objectForKey:string]
for(NSString *string in [stringsToManagers allKeys])
{
if([type rangeOfString:string].location != NSNotFound)
{
[[stringsToManagers objectForKey:string] addNewObject];
// or get it and store it wherever it should go
}
}
If all the managers do is vend appropriate objects, the more object-oriented approach might be:
NSDictionary *stringsToClasses =
[NSDictionary dictionaryWithObjectsAndKeys:
#"coin-large", [LargeCoin class],
#"coin-small", [SmallCoin class],
nil];
// this is assuming that type may contain multiple types; otherwise
// just use [stringsToManagers objectForKey:string]
for(NSString *string in [stringsToManagers allKeys])
{
if([type rangeOfString:string].location != NSNotFound)
{
id class = [stringsToManagers objectForKey:string];
id newObject = [[class alloc] init];
// this is exactly the same as if, for example, you'd
// called [[LargeCoin alloc] init] after detecting coin-large
// within the input string; you should obviously do something
// with newObject now
}
}
That could save you having to write any managers if your program structure otherwise fits.

looping through an NSMutableDictionary

How do I loop through all objects in a NSMutableDictionary regardless of the keys?
A standard way would look like this
for(id key in myDict) {
id value = [myDict objectForKey:key];
[value doStuff];
}
you can use
[myDict enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
// do something with key and obj
}];
if your target OS supports blocks.
You can use [dict allValues] to get an NSArray of your values. Be aware that it doesn't guarantee any order between calls.
For simple loop, fast enumeration is a bit faster than block-based loop
It's easier to do concurrent or reverse enumeration with block-based enumeration than with fast enumeration
When looping with NSDictionary you can get key and value in one hit with a block-based enumerator, whereas with fast enumeration you have to use the key to retrieve the value in a separate message send
in fast enumeration
for(id key in myDictionary) {
id value = [myDictionary objectForKey:key];
// do something with key and obj
}
in Blocks :
[myDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// do something with key and obj
}];
You don't need to assign value to a variable. You can access it directly with myDict[key].
for(id key in myDict) {
NSLog(#"Key:%# Value:%#", key, myDict[key]);
}
Another way is to use the Dicts Enumerator. Here is some sample code from Apple:
NSEnumerator *enumerator = [myDictionary objectEnumerator];
id value;
while ((value = [enumerator nextObject])) {
/* code that acts on the dictionary’s values */
}