Copying NSMutableDictionary through pointers - iphone

What I'm trying to do is search an array of dictionaries for a specific target dictionary, and if found, replace the actual dictionary in the original array with the target dictionary. The search algorithim works, but the copying of dictionaries doesn't. The main line in question is the one that says:
tempDict=targetDict;
I was hoping that tempDict would be a pointer to the original dictionary from the original array, but when trying to log the author name, I get "moe" instead of "steve".
-(void)viewDidAppear
{
[actualDictionary setObject:#"test" forKey:#"mainNote"];
[actualDictionary setObject:#"moe" forKey:#"authorName"];
[targetDictionary setObject:#"test" forKey:#"mainNote"];
[targetDictionary setObject:#"steve" forKey:#"authorName"];
[arrayOfNotes addObject:actualDictionary];
[self beginSearchWithMainArray];
}
-(void)beginSearchWithMainArray;
{
[self searchArray:arrayOfNotes forDict:targetDictionary];
}
-(void)searchArray:(NSMutableArray*)array forDict:(NSMutableDictionary*)targetDict
{
NSString *targetText=[targetDict objectForKey:#"mainNote"];
for(int i=0;i<[array count];i++)
{
NSMutableDictionary *tempDict=[array objectAtIndex:i];
NSString *possibleText=[tempDict objectForKey:#"mainNote"];
if([possibleText isEqualToString:targetText])
{
//found match, replace tempDict with targetDict
tempDict=targetDict;
NSLog(#"found match");
NSString *authorName=[[arrayOfNotes objectAtIndex:0] objectForKey:#"authorName"];
NSLog(#"%#", authorName); //should be steve
return;
}
//no match, search sub notes
...
}
}

replace
tempDict=targetDict;
with
[array replaceObjectAtIndex:i withObject:targetDict];
or
[tempDict setDictionary:targetDict]
tempDict is a pointer to a NSMutableDictionary, but assign it to another instance doesn't means change the content of the previous instance
you have to modify what "pointer point to" not the "pointer", thats why you can use setDictionary: to do the "assignment"

tempDict is just a reference to the matched element of the array. Changing its value will not modify the array. Replace
tempDict=targetDict;
to
[array replaceObjectAtIndex:i withObject:targetDict];

Related

Trying to add many arrays to one NSMutableArray

I'm Trying here to add many arrays to one NSMutableArray
Actually am adding the same array with different values Many Times to one NSMutable Array
this the code:
NSMutableArray* wordsArray =[[NSMutableArray alloc] init ];
NSMutableArray* meaningsArray=[[NSMutableArray alloc]init];
NSMutableArray* wordsArrayTemp=[[NSMutableArray alloc]init];
NSMutableArray* meaningsArrayTemp=[[NSMutableArray alloc]init ];
NSMutableArray* allWords =[[NSMutableArray alloc]initWithCapacity:2];
NSMutableArray* allMeanings=[[NSMutableArray alloc]initWithCapacity:2];
for(int i=0;i<2;i++)
{
int wordsCounter=0;
[wordsArrayTemp removeAllObjects];
[meaningsArrayTemp removeAllObjects];
for(NSString *tmp in wordsArray)
{
NSString *meaning =[meaningsArray objectAtIndex:wordsCounter++];
subtmp= [ tmp substringWithRange:NSMakeRange(0,1)];
if([currentTable isEqualToString:#"arabicToEnglish"])
{
if([subtmp isEqualToString:[arabicLetters objectAtIndex:i]])
{
[wordsArrayTemp addObject:tmp];
[meaningsArrayTemp addObject:meaning];
}
}
else
if([subtmp isEqualToString:[englishLetters objectAtIndex:i]])
{
[wordsArrayTemp addObject:tmp];
[meaningsArrayTemp addObject:meaning];
}
}
[allWords insertObject:wordsArrayTemp atIndex:i];
// [allWords addObject: wordsArrayTemp];
[allMeanings addObject:meaningsArrayTemp];
NSLog(#"all words count%i",[[allWords objectAtIndex:i] count]);
}
The Problem :
The supposed behavior here is to have 2 different values in the allWords array .
But what Actually happens is that the 2 values filled with the same value with the last index Value.
I mean [allWords objectAtIndex:0] should have 2000 object and [allWords objectAtIndex:1] should have 3000 ,but what happens that they both have 3000 object !!
what am I missing here?!!
thnx
when you add an object to an array the object is not copied. You just save its memory address.
Basically you added the same temporary array to the parent array. And you did all your array manipulations to the same array.
Maybe this piece of unrolled loop code will make it a little bit clearer.
// create new array on a specific memory address. let's say this address is 0x01
NSMutableArray* wordsArrayTemp=[[NSMutableArray alloc]init];
// first iteration of your loop
// remove all objects from array at memory address 0x01
[wordsArrayTemp removeAllObjects];
// add objects to the array at address 0x01
[wordsArrayTemp addObject:tmp];
// insert array (still at address 0x01) to the parent array
[allWords insertObject:wordsArrayTemp atIndex:i];
// your allWords array now looks like this: {array#0x01}
// second iteration of your loop
// remove all objects from array at memory address 0x01!!! (still the same array as in the first iteration)
// since it's the same array all objects from the array at [allWords objectAtIndex:0] are removed too
[wordsArrayTemp removeAllObjects];
// add objects to the array at address 0x01
[wordsArrayTemp addObject:tmp];
// insert array (still at address 0x01) to the parent array
[allWords insertObject:wordsArrayTemp atIndex:i];
// your allWords array now looks like this {array#0x01, array#0x01}
the solution is pretty easy.
At the beginning of the for-loop instead of removing allObjects from the array create new arrays.
Just replace
[wordsArrayTemp removeAllObjects];
[meaningsArrayTemp removeAllObjects];
with
wordsArrayTemp = [NSMutableArray array];
meaningsArrayTemp = [NSMutableArray array];
Try to add one array simutaneously :
[[allWords array] addObject:wordsArray.array];
Hope it'll help
Try this:-
[allWords insertObject:[wordsArrayTemp copy] atIndex:i];
It should work.

How do I find (not remove) duplicates in an NSDictionary of NSArrays?

The title pretty much says it all, but just to clarify: I have an NSMutableDictonary containing several NSMutableArrays. What I would like to do is find any value that is present in multiple arrays (there will not be any duplicates in a single array) and return that value. Can someone please help? Thanks in advance!
Edit: For clarity's sake I will specify some of my variables:
linesMutableDictionary contains a list of Line objects (which are a custom NSObject subclass of mine)
pointsArray is an array inside each Line object and contains the values I am trying to search through.
Basically I am trying to find out which lines share common points (the purpose of my app is geometry based)
- (NSValue*)checkForDupes:(NSMutableDictionary*)dict {
NSMutableArray *derp = [NSMutableArray array];
for (NSString *key in [dict allKeys]) {
Line *temp = (Line*)[dict objectForKey:key];
for (NSValue *val in [temp pointsArray]) {
if ([derp containsObject:val])
return val;
}
[derp addObjectsFromArray:[temp pointsArray]];
}
return nil;
}
this should work
If by duplicates you mean returning YES to isEqual: you could first make an NSSet of all the elements (NSSet cannot, by definition, have duplicates):
NSMutableSet* allElements = [[NSMutableSet alloc] init];
for (NSArray* array in [dictionary allValues]) {
[allElements addObjectsFromArray:array];
}
Now you loop through the elements and check if they are in multiple arrays
NSMutableSet* allDuplicateElements = [[NSMutableSet alloc] init];
for (NSObject* element in allElements) {
NSUInteger count = 0;
for (NSArray* array in [dictionary allValues]) {
if ([array containsObject:element]) count++;
if (count > 1) {
[allDuplicateElements addObject:element];
break;
}
}
}
Then you have your duplicate elements and don't forget to release allElements and allDuplicateElements.

How to rename a Key in NSMutableDictionary?

I am having a NSMutableDictionary. I have to dynamically rename any Key in the dictionary to a new value, in my code.. I can't find any built-in API to do this..
How can I do this? Is there any built-in API available to do this?
Thanks everyone..
// assumes that olkdey and newkey won't be the same; they can't as
// constants... but...
[dict setObject: [dict objectForKey: #"oldkey"] forKey: #"newkey"];
[dict removeObjectForKey: #"oldkey"];
Think about what "directly editing an existing key" means. A dictionary is a hash; it hashes the contents of the keys to find a value.
What happens if you were to change the contents of a key? The key would need to be rehashed (and the dictionary's internal structures re-balanced) or the value would no longer be retrievable.
Why do you want to edit the contents of a key in the first place? I.e. what problem does that solve that the above does not?
This should work:
- (void) renameKey:(id<NSCopying>)oldKey toKey:(id<NSCopying>)newKey{
NSObject *object = [dictionary objectForKey:oldKey];
[object retain];
[dictionary removeObjectForKey:oldKey];
[dictionary setObject:object forKey:newKey];
[object release];
}
This does exactly the same as bbum's answer but, if you remove the old key first (like in this example) then you have to retain the object temporarily otherwise it might get deallocated in the way ;)
Conclusion: Unless you need explicitly to remove the old key first do as bbum.
#interface NSMutableDictionary (KAKeyRenaming)
- (void)ka_replaceKey:(id)oldKey withKey:(id)newKey;
#end
#implementation NSMutableDictionary (KAKeyRenaming)
- (void)ka_replaceKey:(id)oldKey withKey:(id)newKey
{
id value = [self objectForKey:oldKey];
if (value) {
[self setObject:value forKey:newKey];
[self removeObjectForKey:oldKey];
}
}
#end
This also handles the case where the dictionary doesn't have a value for the key nicely.
I have to navigate a complete JSON response object that holds fields, sub-dictionaries and sub-arrays. All because one of the JSON fields is called "return" which is an iOS reserved word, so can't be used with the JSONModel Cocoa Pod.
Here's the code:
+ (id) sanitizeJSON:(id) dictIn {
if (dictIn) //check not null
{
// if it's a dictionary item
if ([dictIn isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *dictOut = [dictIn mutableCopy];
// Do the fix replace "return" with "not_return"
if ([dictOut objectForKey: #"return"])
{[dictOut setObject: [dictIn objectForKey: #"return"] forKey: #"not_return"];
[dictOut removeObjectForKey: #"return"];}
// Continue the recursive walk through
NSArray*keys=[dictOut allKeys]; //get all the keys
for (int n=0;n<keys.count;n++)
{
NSString *key = [keys objectAtIndex:n];
//NSLog(#"key=%# value=%#", key, [dictOut objectForKey:key]);
if (([[dictOut objectForKey:key] isKindOfClass:[NSDictionary class]]) || ([[dictOut objectForKey:key] isKindOfClass:[NSArray class]]))
{
// recursive call
id sanitizedObject = [self sanitizeJSON:[dictOut objectForKey:key]];
[dictOut removeObjectForKey: key];
[dictOut setObject:sanitizedObject forKey:key];
// replace returned (poss modified) item with this one
}
}
return dictOut; //return dict
}
else if ([dictIn isKindOfClass:[NSArray class]]) //Or if it's an array item
{
NSMutableArray *tempArray = [dictIn mutableCopy];
// Do the recursive walk across the array
for (int n=0;n< tempArray.count; n++)
{
// if array item is dictionary
if (([[tempArray objectAtIndex:n] isKindOfClass:[NSDictionary class]]) || ([[tempArray objectAtIndex:n] isKindOfClass:[NSArray class]]))
{
// recursive call
id sanitizedObject = [self sanitizeJSON:[tempArray objectAtIndex:n]];
// replace with the possibly modified item
[tempArray replaceObjectAtIndex:n withObject:sanitizedObject];
}
}
return tempArray; //return array
}
return dictIn; //Not nil or dict or array
}
else
return dictIn; //return nil
}

How to check if the value in an NSDictionary exists in an array of dictionarys

The title is a bit confusing...I'll explain
I have an NSMutableArray I am populating with NSMutableDictionary objects. What I am trying to do is before the dictionary object is added to the array, I need to check whether any of the dictionaries contain a value equal to an id that is already set.
Example:
Step 1: A button is clicked setting the id of an object for use in establishing a view.
Step 2: Another button is pressed inside said view to save some of its contents into a dictionary, then add said dictionary to an array. But if the established ID already exists as a value to one of the dictionaries keys, do not insert this dictionary.
Here is some code I have that is currently not working:
-(IBAction)addToFavorites:(id)sender{
NSMutableDictionary *fav = [[NSMutableDictionary alloc] init];
[fav setObject:[NSNumber numberWithInt:anObject.anId] forKey:#"id"];
[fav setObject:#"w" forKey:#"cat"];
if ([dataManager.anArray count]==0) { //Nothing exists, so just add it
[dataManager.anArray addObject:fav];
}else {
for (int i=0; i<[dataManager.anArray count]; i++) {
if (![[[dataManager.anArray objectAtIndex:i] objectForKey:#"id"] isEqualToNumber:[NSNumber numberWithInt:anObject.anId]]) {
[dataManager.anArray addObject:fav];
}
}
}
[fav release];
}
One fairly easy way to do this kind of check is to filter the array using an NSPredicate. If there's no match, the result of filtering will be an empty array. So for example:
NSArray *objs = [dataManager anArray];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", [NSNumber numberWithInt:i]];
NSArray *matchingObjs = [objs filteredArrayUsingPredicate:predicate];
if ([matchingObjs count] == 0)
{
NSLog(#"No match");
}

NSDictionary: convert NSString keys to lowercase to search a string on them

I'm developing an iPhone application.
I use a NSDictionary to store city's names as key, and population as value. I want to search the keys using lowercase.
I've using this:
NSDictionary *dict;
[dict objectForKey:[[city stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString]];
But, it doesn't work.
I know, I can do a for, convert keys to lowercase and compare with city.
Is there any other way to do that? Maybe, with a NSDictionary method.
UPDATE The NSDictionary is loaded from a property list.
Thank you.
I use this method in an NSDictionary category.
#implementation NSDictionary (MyCategory)
- (NSDictionary *)dictionaryWithLowercaseKeys {
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:0];
NSString *key;
for (key in self) {
[result setObject:[self objectForKey:key] forKey:[key lowercaseString]];
}
return result;
}
#end
Although I'm still not clear on what you want, this loop with search for keys case insensitively. Getting the value of that key is then trivial.
for key in dict
{
if ([key caseInsensitiveCompare: #"Whatever"] == NSOrderedSame)
NSLog(#"They are equal.");
}
I'd say create a second dictionary. After you load from the property set, loop through that dictionary and insert objects into the second dictionary. Convert the keys to lowercase as you go. Then release the first dictionary.
The above answers only work for a flat dictionary without any nesting whatsoever. I created a category that creates a copy of an existing NSDictionary with every key converted to lower case.
The header: NSDictionary+LowercaseKeys.h
#import <Foundation/Foundation.h>
#interface NSDictionary (LowercaseKeys)
/*
Recursive algorithm to find all nested dictionary keys and create an NSMutableDictionary copy with all keys converted to lowercase.
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
+ (NSMutableDictionary *)dictionaryWithLowercaseKeysFromDictionary:(NSDictionary *)dictionary;
/*
Convienience method to create a new lowercase dictionary object an existing NSDictionary instance
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
- (NSMutableDictionary *)dictionaryWithLowercaseKeys;
#end
The Implementation: NSDictionary+LowercaseKeys.m
#import "NSDictionary+LowercaseKeys.h"
#implementation NSDictionary (LowercaseKeys)
/*
Recursive algorithm to find all nested dictionary keys and create an NSMutableDictionary copy with all keys converted to lowercase
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
+ (NSMutableDictionary *)dictionaryWithLowercaseKeysFromDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[dictionary count]];
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
// There are 3 types of objects to consider, NSDictionary, NSArray and everything else
id resultObj;
if ([obj isKindOfClass:NSDictionary.class])
{
// Recursively dig deeper into this nested dictionary
resultObj = [NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:obj];
}
else if ([obj isKindOfClass:NSArray.class])
{
/*
Iterate over this nested NSArray. Recursively convert any NSDictionary objects to the lowercase version.
If the array contains another array then continue to recursively dig deeper.
*/
resultObj = [NSMutableArray arrayWithCapacity:[obj count]];
for (id arrayObj in obj)
{
if ([arrayObj isKindOfClass:NSDictionary.class])
[resultObj addObject:[NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:arrayObj]];
else if ([arrayObj isKindOfClass:NSArray.class])
[resultObj addObject:[NSMutableDictionary arrayWithLowercaseKeysForDictionaryArray:arrayObj]];
else
[resultObj addObject:arrayObj];
}
}
else
{
// The object is not an NSDictionary or NSArray so keep the object as is
resultObj = obj;
}
// The result object has been converted and can be added to the dictionary. Note this object may be nested inside a larger dictionary.
[resultDict setObject:resultObj forKey:[key lowercaseString]];
}];
return resultDict;
}
/*
Convienience method to create a new dictionary object with all lowercase keys from an existing instance
*/
- (NSMutableDictionary *)dictionaryWithLowercaseKeys
{
return [NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:self];
}
#pragma mark - Private helpers
/*
Convert NSDictionary keys to lower case when embedded in an NSArray
*/
+ (NSMutableArray *)arrayWithLowercaseKeysForDictionaryArray:(NSArray *)dictionaryArray
{
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[dictionaryArray count]];
for (id eachObj in dictionaryArray)
{
if ([eachObj isKindOfClass:NSDictionary.class])
[resultArray addObject:[NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:eachObj]];
else if ([eachObj isKindOfClass:NSArray.class])
[resultArray addObject:[NSMutableDictionary arrayWithLowercaseKeysForDictionaryArray:eachObj]];
}
return resultArray;
}
#end