NSManagedObject to NSDictionary - objective-c

Trying to serialise NSManagedObject to NSDictionary including related data.
I found some code for that here:
http://vladimir.zardina.org/2010/03/serializing-archivingunarchiving-an-nsmanagedobject-graph/
Unfortunately, there is no support for NSOrderedSet. Tried to implement it myself, but have a crash with message doesn't recognise selector on line if (!relatedObject.traversed) {.
- (NSDictionary*) toDictionary
{
self.traversed = YES;
NSArray* attributes = [[[self entity] attributesByName] allKeys];
NSArray* relationships = [[[self entity] relationshipsByName] allKeys];
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:
[attributes count] + [relationships count] + 1];
[dict setObject:[[self class] description] forKey:#"class"];
for (NSString* attr in attributes) {
NSObject* value = [self valueForKey:attr];
if (value != nil) {
[dict setObject:value forKey:attr];
}
}
for (NSString* relationship in relationships) {
NSObject* value = [self valueForKey:relationship];
if ([value isKindOfClass:[NSSet class]]) {
// To-many relationship
// The core data set holds a collection of managed objects
NSSet* relatedObjects = (NSSet*) value;
// Our set holds a collection of dictionaries
NSMutableSet* dictSet = [NSMutableSet setWithCapacity:[relatedObjects count]];
for (ExtendedManagedObject* relatedObject in relatedObjects) {
if (!relatedObject.traversed) {
[dictSet addObject:[relatedObject toDictionary]];
}
}
[dict setObject:dictSet forKey:relationship];
}
else if ([value isKindOfClass:[NSOrderedSet class]]) {
// To-many relationship
// The core data set holds a collection of managed objects
NSOrderedSet* relatedObjects = (NSOrderedSet *)value;
// Our set holds a collection of dictionaries
NSMutableSet* dictSet = [NSMutableSet setWithCapacity:[relatedObjects count]];
for (ExtendedManagedObject* relatedObject in relatedObjects) {
if (!relatedObject.traversed) {
[dictSet addObject:[relatedObject toDictionary]];
}
}
[dict setObject:dictSet forKey:relationship];
}
else if ([value isKindOfClass:[ExtendedManagedObject class]]) {
// To-one relationship
ExtendedManagedObject* relatedObject = (ExtendedManagedObject*) value;
if (!relatedObject.traversed) {
// Call toDictionary on the referenced object and put the result back into our dictionary.
[dict setObject:[relatedObject toDictionary] forKey:relationship];
}
}
}
return dict;
}
- (void) populateFromDictionary:(NSDictionary*)dict
{
NSManagedObjectContext* context = [self managedObjectContext];
for (NSString* key in dict) {
if ([key isEqualToString:#"class"]) {
continue;
}
NSObject* value = [dict objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
// This is a to-one relationship
ExtendedManagedObject* relatedObject =
[ExtendedManagedObject createManagedObjectFromDictionary:(NSDictionary*)value
inContext:context];
[self setValue:relatedObject forKey:key];
}
else if ([value isKindOfClass:[NSSet class]]) {
// This is a to-many relationship
NSSet* relatedObjectDictionaries = (NSSet*) value;
// Get a proxy set that represents the relationship, and add related objects to it.
// (Note: this is provided by Core Data)
NSMutableSet* relatedObjects = [self mutableSetValueForKey:key];
for (NSDictionary* relatedObjectDict in relatedObjectDictionaries) {
ExtendedManagedObject* relatedObject =
[ExtendedManagedObject createManagedObjectFromDictionary:relatedObjectDict
inContext:context];
[relatedObjects addObject:relatedObject];
}
}
else if (value != nil) {
// This is an attribute
[self setValue:value forKey:key];
}
}
}

it is fast and easy way
NSMutableArray *array = [NSMutableArray arrayWithCapacity:ManagedObjectItems.count];
[[ManagedObjectItems allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Diary_item_food *food = obj;
NSArray *keys = [[[food entity] attributesByName] allKeys];
NSDictionary *dict = [obj dictionaryWithValuesForKeys:keys];
[array addObject:dict];
}];

I found the ready gist on Gihub: https://gist.github.com/nuthatch/5607405

Even easier way, query for the objectID and use NSDictionaryResultType on the fetch request.
Update: Only if you don't need related data.

Related

Path for key in NSDictionary

In my app I need to insert values into NSDictionary having only last path component. E.g. my given dict is
a = 1;
b = 2;
c = 3;
d = {
e = 4;
f = 5;
g = {
h = 6;
};
};
j = {
k = 7;
};
and I need to change value for k. Path components should be #[#"j", #"k"]. I tried it with something like that:
- (void)recurse:(NSDictionary*)dict keyToFound:(NSString*)ktf stack:(NSMutableArray*)stack parent:(NSString*)parent
{
for (NSString *key in [dict allKeys]) {
if ([key isEqualToString:ktf]) {
[stack insertObject:key atIndex:[stack count]];
return;
}
else {
if ([[dict valueForKey:key] isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [dict valueForKey:key];
[stack insertObject:key atIndex:[stack count]];
[self recurse:d keyToFound:ktf stack:stack parent:key];
//[stack removeObject:key];
}
}
}
}
but, obviuos, it's a wrong way.
Here are two simpler ways to set the value in nested dictionaries. The first assumes that you know that the keys exist and could be C identifiers. In this case using setValue:forKeyPath: is the best approach. Otherwise a simple loop in function (or a Method or a category on NSDictionary) will do the trick:
void setValueForPathComponentsOfDictionary(id value, NSArray *components, NSMutableDictionary *dict) {
NSMutableArray *parts = [components mutableCopy];
id lastPart = parts.lastObject;
[parts removeLastObject];
for (id part in parts) {
if (![dict respondsToSelector:#selector(objectForKey:)])
return; // Silently fail.
dict = [dict objectForKey:part];
}
if ([dict respondsToSelector:#selector(objectForKey:)])
[dict setValue:value forKey:lastPart];
}
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableDictionary *dict = [#{
#"a":[#{#"a":#1,#"b":#2} mutableCopy],
#"b":[#{#"a":#3,#"b":#4} mutableCopy]
} mutableCopy];
NSLog(#"Initial Dictionary: %#", dict);
[dict setValue:#9 forKeyPath:#"a.b"];
NSLog(#"After setValue:forKeyPath: %#", dict);
setValueForPathComponentsOfDictionary(#0, #[#"b",#"a"], dict);
NSLog(#"After setValueForPathComponentsOfDictionary %#", dict);
}
return 0;
}
Ok, I've found the solution.
- (BOOL)recurse:(NSDictionary*)dict keyToFound:(NSString*)ktf stack:(NSMutableArray*)stack parent:(NSString*)parent
{
BOOL result = NO;
for (NSString *key in [dict allKeys]) {
if ([key isEqualToString:ktf]) {
[stack insertObject:key atIndex:[stack count]];
return YES;
}
else {
if ([[dict valueForKey:key] isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [dict valueForKey:key];
[stack insertObject:key atIndex:[stack count]];
result = [self recurse:d keyToFound:ktf stack:stack parent:key];
if (!result) {
[stack removeLastObject];
}
}
}
}
return result;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:#1,#"a",
#1,#"b",
#2,#"c",
[NSMutableDictionary dictionaryWithObjectsAndKeys:#4, #"e",
#5, #"f",
[NSMutableDictionary dictionaryWithObject:#6 forKey:#"h"], #"g", nil],#"d",
[NSMutableDictionary dictionaryWithObject:#7 forKey:#"k"],#"i",nil];
NSLog(#"Before %#",dict);
NSString *keyToChange = #"e";
NSString *valueToChange = #"Your New Value";
[dict enumerateKeysAndObjectsUsingBlock:^(id key ,id obj, BOOL *stop){
if([key isEqualToString:keyToChange])
{
NSLog(#"Found : %#",keyToChange);
if(![obj isKindOfClass:[NSArray class]] || ![obj isKindOfClass:[NSDictionary class]] || ![obj isKindOfClass:[NSMutableDictionary class]])
{
[dict setValue:#"newval" forKey:keyToChange];
return;
}
}
if([obj isKindOfClass:[NSDictionary class]])
{
[obj enumerateKeysAndObjectsUsingBlock:^(id key ,id obj1, BOOL *stop){
if([key isEqualToString:keyToChange])
{
if(![obj isKindOfClass:[NSArray class]] || ![obj isKindOfClass:[NSDictionary class]] || ![obj isKindOfClass:[NSMutableDictionary class]])
{
NSLog(#"Found : %#",keyToChange);
[obj setValue:#"newval" forKey:keyToChange];
return;
}
}
}];
}
}];
NSLog(#"After %#",dict);

Replace all NSNull objects in an NSDictionary

I'm curious, I currently have an NSDictionary where some values are set to an NSNull object thanks to the help of json-framework.
The aim is to strip all NSNull values and replace it with an empty string.
I'm sure someone has done this somewhere? No doubt it is probably a four liner and is simple, I am just far too burnt out to figure this out on my own.
I've made a few changes to Jacob's original answer to extend it to handle dictionaries and arrays stored within the original dictionary.
#import "NSDictionary+NullReplacement.h"
#import "NSArray+NullReplacement.h"
#implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
#end
And there's also an array category of course:
#import "NSArray+NullReplacement.h"
#import "NSDictionary+NullReplacement.h"
#implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
#end
With this, you can take any array or dictionary and recursively wipe out all the [NSNull null] instances.
P.S. For completion's sake, here are the header files:
#interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
#end
And the array header:
#interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
#end
Really simple:
#interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
#end
#implementation NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
#end
Usage:
NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Rolling through the dictionary hunting for NSNull is one way to tackle the problem, but I took a slightly lazier approach. Instead of nil you could assign an empty string, but the principle is the same.
CPJSONDictionary.h
#interface NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey;
#end
CPJSONDictionary.m
#implementation NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey {
id object = [self objectForKey:aKey];
if ([object isKindOfClass:[NSNull class]]) {
object = nil;
}
return object;
}
#end
I have tested Stakenborg solution. It works well, but it has following problem. If some object is expected to be number, for instance, converting it to NSNull can be a source of error.
I have create a new method to directly remove the NSNull entries. This way you only have to check that correspondant key exists.
Add in NSDictionary+NullReplacement
- (NSDictionary *)dictionaryByRemovingNulls{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced removeObjectForKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByRemovingNulls] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByRemovingNulls] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
And in NSArray+NullReplacement
- (NSArray *)arrayByRemovingNulls {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (int idx = [replaced count]-1; idx >=0; idx--) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced removeObjectAtIndex:idx];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByRemovingNulls]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByRemovingNulls]];
}
return [replaced copy];
}
another variation:
NSDictionary * NewDictionaryReplacingNSNullWithEmptyNSString(NSDictionary * dict) {
NSMutableDictionary * const m = [dict mutableCopy];
NSString * const empty = #"";
id const nul = [NSNull null];
NSArray * const keys = [m allKeys];
for (NSUInteger idx = 0, count = [keys count]; idx < count; ++idx) {
id const key = [keys objectAtIndex:idx];
id const obj = [m objectForKey:key];
if (nul == obj) {
[m setObject:empty forKey:key];
}
}
NSDictionary * result = [m copy];
[m release];
return result;
}
The result is the same as, and it appears pretty much identical to Jacob's, but the speed and memory requirements are one half to one third (ARC or MRC) in the tests I made. Of course, you could also use it as a category method as well.
Here is my solution:
+ (NSDictionary *)cleanNullInJsonDic:(NSDictionary *)dic
{
if (!dic || (id)dic == [NSNull null])
{
return dic;
}
NSMutableDictionary *mulDic = [[NSMutableDictionary alloc] init];
for (NSString *key in [dic allKeys])
{
NSObject *obj = dic[key];
if (!obj || obj == [NSNull null])
{
// [mulDic setObject:[#"" JSONValue] forKey:key];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
[mulDic setObject:[self cleanNullInJsonDic:(NSDictionary *)obj] forKey:key];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *array = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulDic setObject:array forKey:key];
}else
{
[mulDic setObject:obj forKey:key];
}
}
return mulDic;
}
+ (NSArray *)cleanNullInJsonArray:(NSArray *)array
{
if (!array || (id)array == [NSNull null])
{
return array;
}
NSMutableArray *mulArray = [[NSMutableArray alloc] init];
for (NSObject *obj in array)
{
if (!obj || obj == [NSNull null])
{
// [mulArray addObject:[#"" JSONValue]];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary *dic = [self cleanNullInJsonDic:(NSDictionary *)obj];
[mulArray addObject:dic];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *a = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulArray addObject:a];
}else
{
[mulArray addObject:obj];
}
}
return mulArray;
}
-(NSDictionary*)stripNulls:(NSDictionary*)dict{
NSMutableDictionary *returnDict = [NSMutableDictionary new];
NSArray *allKeys = [dict allKeys];
NSArray *allValues = [dict allValues];
for (int i=0; i<[allValues count]; i++) {
if([allValues objectAtIndex:i] == (NSString*)[NSNull null]){
[returnDict setValue:#"" forKey:[allKeys objectAtIndex:i]];
}
else
[returnDict setValue:[allValues objectAtIndex:i] forKey:[allKeys objectAtIndex:i]];
}
return returnDict;
}
A category on nsnull that returns nil seems to also sense, at least to me. There are a few out there. One makes all calls return nil which seems to make sense. Sorry no link. I guess if you need to later use nspropertylistserialization the category might not work for you.

changing a key name in NSDictionary

I have a method which returns me a nsdictionary with certain keys and values. i need to change the key names from the dictionary to a new key name but the values need to be same for that key,but i am stuck here.need help
This method will only work with a mutable dictionary. It doesn't check what should be done if the new key already exists.
You can get a mutable dictionary of a immutable by calling mutableCopy on it.
- (void)exchangeKey:(NSString *)aKey withKey:(NSString *)aNewKey inMutableDictionary:(NSMutableDictionary *)aDict
{
if (![aKey isEqualToString:aNewKey]) {
id objectToPreserve = [aDict objectForKey:aKey];
[aDict setObject:objectToPreserve forKey:aNewKey];
[aDict removeObjectForKey:aKey];
}
}
You can't change anything in an NSDictionary, since it is read only.
How about loop through the dictionary and create a new NSMutableDictionary with the new key names ?
Could you not add a new key-value pair using the old value, and then remove the old key-value pair?
This would only work on an NSMutableDictionary. NSDictionarys are not designed to be changed once they have been created.
To change specific key to new key, I have written a recursive method for Category Class.
- (NSMutableDictionary*)replaceKeyName:(NSString *)old_key with:(NSString )new_key {
NSMutableDictionary dict = [NSMutableDictionary dictionaryWithDictionary: self];
NSMutableArray *keys = [[dict allKeys] mutableCopy];
for (NSString key in keys) {
if ([key isEqualToString:old_key]) {
id val = [self objectForKey:key];
[dict removeObjectForKey:key];
[dict setValue:val forKey:new_key];
return dict;
} else {
const id object = [dict objectForKey: key];
if ([object isKindOfClass:[NSDictionary class]]) {
[dict setObject:[dict replaceKeyName:old_key with:new_key] forKey:key];
} else if ([object isKindOfClass:[NSArray class]]){
if (object && [(NSArray)object count] > 0) {
NSMutableArray *arr_temp = [[NSMutableArray alloc] init];
for (NSDictionary *temp_dict in object) {
NSDictionary *temp = [temp_dict replaceKeyName:old_key with:new_key];
[arr_temp addObject:temp];
}
[dict setValue:arr_temp forKey:key];
}
}
}
}
return dict;
}

Deep combine NSDictionaries

I need to merge two NSDictionarys into one provided that if there are dictionaries within the dictionaries, they are also merged.
More or less like jQuery's extend function.
NSDictionary+Merge.h
#import <Foundation/Foundation.h>
#interface NSDictionary (Merge)
+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2;
- (NSDictionary *) dictionaryByMergingWith: (NSDictionary *) dict;
#end
NSDictionary+Merge.m
#import "NSDictionary+Merge.h"
#implementation NSDictionary (Merge)
+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];
[dict2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
if (![dict1 objectForKey:key]) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
[result setObject: newVal forKey: key];
} else {
[result setObject: obj forKey: key];
}
}
}];
return (NSDictionary *) [[result mutableCopy] autorelease];
}
- (NSDictionary *) dictionaryByMergingWith: (NSDictionary *) dict {
return [[self class] dictionaryByMerging: self with: dict];
}
#end
I think this is what you're looking for:
First, you need to make a deep mutable copy, so you can create a category on NSDictionary to do this:
#implementation NSDictionary (DeepCopy)
- (id)deepMutableCopy
{
id copy(id obj) {
id temp = [obj mutableCopy];
if ([temp isKindOfClass:[NSArray class]]) {
for (int i = 0 ; i < [temp count]; i++) {
id copied = [copy([temp objectAtIndex:i]) autorelease];
[temp replaceObjectAtIndex:i withObject:copied];
}
} else if ([temp isKindOfClass:[NSDictionary class]]) {
NSEnumerator *enumerator = [temp keyEnumerator];
NSString *nextKey;
while (nextKey = [enumerator nextObject])
[temp setObject:[copy([temp objectForKey:nextKey]) autorelease]
forKey:nextKey];
}
return temp;
}
return (copy(self));
}
#end
Then, you can call deepMutableCopy like this:
NSMutableDictionary *someDictionary = [someDict deepMutableCopy];
[someDictionary addEntriesFromDictionary:otherDictionary];
I added this to the code mentioned above. It may not be fully correct, but it handles the case where 2 dict has an element that 1 dict does not.
+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];
NSMutableDictionary * resultTemp = [NSMutableDictionary dictionaryWithDictionary:dict1];
[resultTemp addEntriesFromDictionary:dict2];
[resultTemp enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
if ([dict1 objectForKey:key]) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
[result setObject: newVal forKey: key];
} else {
[result setObject: obj forKey: key];
}
}
else if([dict2 objectForKey:key])
{
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary * newVal = [[dict2 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
[result setObject: newVal forKey: key];
} else {
[result setObject: obj forKey: key];
}
}
}];
return (NSDictionary *) [[result mutableCopy] autorelease];
}
I came here looking for a copy of jQuery's extend but I ended up writing my own implementation. It's a super simple implementation, I did it so I'd understand a way to do it.
+(NSDictionary*) dictionaryByExtending:(NSDictionary*)baseDictionary WithDictionary:(NSDictionary*)extensionDictionary {
NSMutableDictionary * resultDictionary = [NSMutableDictionary dictionaryWithDictionary:baseDictionary];
[extensionDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
BOOL isDict = [obj isKindOfClass:[NSDictionary class]];
BOOL hasValue = [baseDictionary hasObjectForKey:key] != nil;
id setObj = obj;
if( hasValue && isDict ) {
BOOL hasDict = [[baseDictionary objectForKey:key] isKindOfClass:[NSDictionary class]];
if( hasDict ) {
NSDictionary * extendedChildDictionary = [NSDictionary dictionaryByExtending:[baseDictionary objectForKey:key] WithDictionary:obj];
setObj = extendedChildDictionary;
}
}
[resultDictionary setObject:setObj forKey:key];
}];
return resultDictionary;
}
-(NSDictionary*) dictionaryByExtendingWithDictionary:(NSDictionary*)extensionDictionary {
return [NSDictionary dictionaryByExtending:self WithDictionary:extensionDictionary];
}
Hopefully someone will find this helpful, it worked in my tests with deep-recursion. I'm using it to extend deep JSON files full of text.
Alexsander Akers works for me except the case where dict2 contains a dictionary that's missing from dict1 - it crashes. I changed the logic to this:
+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];
[dict2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
if (![dict1 objectForKey:key]) {
[result setObject: obj forKey: key];
} else if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
[result setObject: newVal forKey: key];
}
}];
return (NSDictionary *) [result mutableCopy];
}
I know this is an old question, but I need to do the same thing: recursively merge two dictionary objects. I need to go a step further and merge any objects that can be merged recursively (the end goal is merging two dictionaries created from plists). I am hosting my solution at https://github.com/bumboarder6/NSDictionary-merge
I am still working on the project, but as of this writing it already works (in limited testing) for recursive dictionary merging. Arrays and Sets are coming soon.
I noticed a few logic errors in some other solutions I have seen for this problem and I hopefully avoided those pitfalls, but critiques are welcome.
Usage is simple:
#import "NSMutableDictionary-merge.h"
NSMutableDictionary* dict1 = [NSMutableDictionary ...];
NSDictionary* dict2 = [NSDictionary ...];
[dict1 mergeWithDictionary:dict2];
#import "NSDictionary+Merge.h"
#implementation NSDictionary (Merge)
+ (NSDictionary *)dictionaryByMerging:(NSDictionary *)src with:(NSDictionary *)new
{
NSMutableDictionary *result = [src mutableCopy];
[new enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]
&& [src[key] isKindOfClass:[NSDictionary class]]) {
result[key] = [src[key] dictionaryByMergingWith:obj];
} else {
result[key] = obj;
}
}];
return [NSDictionary dictionaryWithDictionary:result];
}
- (NSDictionary *)dictionaryByMergingWith:(NSDictionary *)dict {
return [[self class] dictionaryByMerging:self with:dict];
}
#end
I needed a way to recursively merge (append) objects within two JSON objects, focusing on the NSDictionaries within, but also considering NSArrays, and gracefully handling when types don't match along the way. The other answers here didn't go that far, and so I needed to write it myself. The following handles all those cases. Because the validation is at the top rather than in the middle it is usable starting with mixed nonnull and nullable objects. It could be expanded in the future to support additional types where appending may apply. To use, rename the xxx_ prefix to your own three digit prefix in lowercase. This is appropriate since this is an extension to a foundation class:
NSObject+Append.h
#interface NSObject (Append)
+ (nullable id)xxx_objectAppendingObject1:(nullable id)object1 object2:(nullable id)object2 NS_SWIFT_NAME(kva_objectAppending(object1:object2:));
#end
NSObject+Append.m
#implementation NSObject (Append)
+ (nullable id)xxx_objectAppendingObject1:(nullable id)object1 object2:(nullable id)object2
{
// VALIDATE ELSE RETURN
if (object1 == nil)
{
return object2;
}
if (object2 == nil)
{
return object1;
}
// MAIN
// dictionary1
NSDictionary *dictionary1 = [object1 isKindOfClass:NSDictionary.class] ? (NSDictionary *)object1 : nil;
// dictionary2
NSDictionary *dictionary2 = [object2 isKindOfClass:NSDictionary.class] ? (NSDictionary *)object2 : nil;
// array1
NSArray *array1 = [object1 isKindOfClass:NSArray.class] ? (NSArray *)object1 : nil;
// array2
NSArray *array2 = [object2 isKindOfClass:NSArray.class] ? (NSArray *)object2 : nil;
// A. NSDICTIONARY TO NSDICTIONARY
if ((dictionary1 != nil) && (dictionary2 != nil))
{
NSMutableDictionary *returnDictionary = dictionary1.mutableCopy;
[dictionary2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop)
{
returnDictionary[key] = [self.class kva_objectAppendingObject1:dictionary1[key] object2:obj];
}];
return returnDictionary;
}
// B. NSARRAY TO NSARRAY
if ((array1 != nil) && (array2 != nil))
{
return [array1.mutableCopy arrayByAddingObjectsFromArray:array2];
}
// DEFAULT
return object2;
}
#end

how to iterate nested dictionaries in objective-c iphone sdk

Hi I have a json string converted unsing the JSON framework into a dictionary and I need to extract its content. How could I iterate to the nested dictionaries? I have already this code that allows me to see the dictionary:
NSDictionary *results = [responseString JSONValue];
NSMutableArray *catArray = [NSMutableArray array];
for (id key in results) {
NSLog(#"key: %#, value: %#", key, [results objectForKey:key]);
[catArray addObject:key];
NSString *cat = key;
}
Could someone provide a sample on how to get to all the levels of the dic not using the name of the keys?
The result structure of the dic is like this: http://www.freeimagehosting.net/uploads/e7c020d697.png
alt text http://www.freeimagehosting.net/uploads/e7c020d697.png
thanks
ldj
You probably have to try recursive function.
-(void)recurse:(NSDictionary *)dict counter: (int*) i parent:(NSString *) parent{
for (NSString* key in [dict allKeys]) {
id value = [dict objectForKey:key];
NSLog(#"%# -> %#", parent, key);
if ([value isKindOfClass:[NSDictionary class]]) {
i++;
NSDictionary* newDict = (NSDictionary*)value;
[self recurse:newDict counter:i parent:key];
i--;
} else {
//Do smthg
}
}
}
this will list all keys and its level.
Try
for ((id) key in [results allKeys]) {
NSLog(#"key: %#, value: %#", key, [results objectForKey:key]);
[catArray addObject:key];
NSString *cat = key;
}
Hope that helped.
EDIT:
i'm not sure if i understand your structure correct, but i'm asuming you have some values in your dictionairy which are again dictionairies.
if you want to interate through a dictionairy which is located inside another one you could do it like this:
for ((id) key in [results allKeys]) {
id value = [results objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary* newDict = (NSDictionary*)value;
for ((id) subKey in [newDict allKeys]) {
...
}
}
}
Of couse if you know the specific key to your desired dictionairy, you can check for that instead of checking if the value is a dict. But maybe it's not a bad idea to check that in both cases...
I'm sure you have solved this at this point, but I found it convenient to write a recursive utility class function that recursively logs every key of a dictionary:
/**
* Recursively logs the keys and values of each object in the dictionary,
* along with the class of the value.
*
* #param dict The dictionary to inspect
*/
+ (void)logDictionaryKeys:(NSDictionary*)dict {
for (id key in dict) {
if ([dict[key] isKindOfClass:[NSDictionary class]]) {
[self logDictionaryKeys:dict[key]];
}else {
NSLog(#"Key: %#", key);
NSLog(#"Value: %# (%#)", dict[key], [dict[key] class]);
}
}
return;
}
The header of the category:
#import <Foundation/Foundation.h>
#interface NSDictionary(Find)
-(id) findflat:(NSString*)keyToFind;
#end
The body:
#import "NSDictionary+Find.h"
#implementation NSDictionary(Find)
-(id) findflat:(NSString*)keyToFind{
if([self objectForKey:keyToFind])
return self[keyToFind];
for(id key in self)
if([[self objectForKey:key] isKindOfClass:[NSDictionary class]])
return [[self objectForKey:key] findflat:keyToFind];
return nil;
}
#end
Usage/Validation:
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
NSDictionary *dic = #{};
XCTAssertNil([dic findflat:#"url"]);
dic = #{#"url":#"http://"};
XCTAssertEqual([dic findflat:#"url"],#"http://");
dic = #{#"other":#4};
XCTAssertNil([dic findflat:#"url"]);
dic = #{#"other":#4,#"url":#"http://"};
XCTAssertEqual([dic findflat:#"url"],#"http://");
dic = #{#"other":#4,#"sd":#"sdf"};
XCTAssertNil([dic findflat:#"url"]);
dic = #{#"other":#4,#"dic":#{#"url":#"http://"}};
XCTAssertEqual([dic findflat:#"url"],#"http://");
dic = #{#"other":#4,#"dic":#{#"sd":#2,#"url":#"http://"}};
XCTAssertEqual([dic findflat:#"url"],#"http://");
dic = #{#"other":#4,#"dic":#{}};
XCTAssertNil([dic findflat:#"url"]);
dic = #{#"other":#4,#"dic":#{#"dic":#{#"sd":#2,#"url":#"http://"}}};
XCTAssertEqual([dic findflat:#"url"],#"http://");
}
I have tried recursion and hope this could help.
P.S. This will return the first occurrence of the key.
+(id)getValue:(NSDictionary*)aDictionary forKey:(NSString *)aKey {
id finalValue = nil;
for (id key in [aDictionary allKeys]) {
id value = aDictionary[key];
if (value != nil && [key isEqualToString:aKey]) {
finalValue = value;
break;
}
else if ([value isKindOfClass:[NSDictionary class]]) {
finalValue = [[self class] getValue:value forKey:aKey];
}
}
return finalValue;
}