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;
}
Related
I'm using CFPropertyListCreateDeepCopy to make a deep mutable copy of an NSDictionary. This example works fine.
NSDictionary *test = #{#"1": #"One"};
NSMutableDictionary *dictionary = (__bridge NSMutableDictionary *)CFPropertyListCreateDeepCopy(NULL, (__bridge CFDictionaryRef)test, kCFPropertyListMutableContainersAndLeaves);
This doesn't work when the NSDictionary uses an NSNumber for a key value. CFPropertyListCreateDeepCopy returns nil. Here is an example.
NSDictionary *test = #{#(1): #"One"};
NSMutableDictionary *dictionary = (__bridge NSMutableDictionary *)CFPropertyListCreateDeepCopy(NULL, (__bridge CFDictionaryRef)test, kCFPropertyListMutableContainersAndLeaves);
How do I make a deep copy of an NSDictionary that has an NSNumber as a key value?
And although NSDictionary and CFDictionary objects allow their keys to be objects of any type, if the keys are not string objects, the collections are not property-list objects.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html#//apple_ref/doc/uid/10000048i-CH3-54303
You cannot copy a property list with a non-string key, because this is no property list.
Simply iterate over the dictionary and copy the items manually.
#implemenatation NSDictionary (DeepCopy)
- (NSMutableDictionary*)deepMutableCopy
{
NSMutableDictionary *copy = [NSMutableDictionary new];
[self enumerateKeysAndObjectsUsingBlock
^(id key, id object)
{
if([object respondsToSelector:#selector(deepMutableCopy)])
{
object = [object deepMutableCopy];
}
else if ([object respondsToSelector:#selector(mutableCopy)])
{
object = [object mutableCopy];
}
else if ([object respondsToSelector:#selector(copy)]) // Maybe, maybe not
{
object = [object copy];
}
[copy setObject:object forKey:key]
}];
return copy;
}
Do the same with NSArray.
(For such tasks I would like to have components in Objective-C.)
I'm trying to set all keys from my NSMutableDictionary as 0, but I'm getting the following error:
__NSDictionaryM was mutated while being enumerated
There's my code:
-(void)marcarTodosRoteiros {
_numeroRoteirosSelecionados = (int)[self.roteirosSelecionados count];
for(id key in self.roteirosSelecionados) {
[self.roteirosSelecionados setObject:#"1" forKey:key];
}
[self.tbFiltros reloadData];
}
This is the enumeration:
for(id key in self.roteirosSelecionados)
and this is the mutation:
[self.roteirosSelecionados setObject:#"1" forKey:key];
You cannot do that. What you should do in this case is get all the keys into a separate array and then set #"1" for each key:
-(void)marcarTodosRoteiros {
NSArray *keys = [self.roteirosSelecionados allKeys];
for (NSString *key in keys)
[self.roteirosSelecionados setObject:#"1" forKey:key];
[self.tbFiltros reloadData];
}
I want to see objects classes of my dictionary in console log. As for standard NSObject subclasses, I override -(NSString*) description in category:
-(NSString*) description
{
NSMutableString* desc = [NSMutableString stringWithFormat: #"<%# 0x%08x>\nobjects count: %ld", [self class], (uint)self, [self count]];
for (id key in [self allKeys])
[desc appendFormat: #"\n%# = %# (%#)", key, [self objectForKey: key], [[self objectForKey: key] class]];
return desc;
}
It works, but only for top-level NSDictionary object (if the object has dictionaries in children they are logged bypassing description method). So NSDictionary prints its children objects in some way without calling description on them...
Is there an approach to log these children dictionaries through my description method?
PS: In practical situation I want to find an object in dictionary that can't be saved to plist. Maybe there is another solution, I would be thankful for that too.
You can write a recursive description method:
// Private Methods
#interface MyClass ()
- (NSString *)_description:(id)object;
#end
...
- (NSString *)_description:(id)object
{
if ([object isKindOfClass:[NSDictionary class]])
{
NSDictionary *dict = (NSDictionary *)object;
NSMutableString *desc = [NSMutableString stringWithFormat: #"<%# %p>\nobjects count: %ld", [dict class], dict, [dict count]];
for (id key in [dict allKeys])
{
[desc appendFormat: #"\n%# = %# (%#)", key, [self _description:[objectForKey: key]], [[self objectForKey: key] class]];
return desc;
}
}
else
{
return [(NSObject *)object description];
}
}
- (NSString *)description
{
return [self _description:self];
}
You'll probably want to pass an incrementing indentation counter so you can format the child objects better, but you should get the idea.
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
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;
}