I create two NSMutableDictionary: dictionary1 and dictionary2. In the first dictionary I store some array object having there keys. And in the second dictionary I store the object of first dictionary with the key like this:
int page = 1;
[dictionary2 setObject:dictionary1 forKey:[NSString stringWithFormat:#"%d",page]];
[dictionary1 removeAllObjects];
but at the time of retrieve of the object i do like this
dictionary1 = [dictionary2 valueForKey:[NSString stringWithFormat:#"%d",page]];
NSLog(#"dictionary1 %#", dictionary1);`
Then it gives null value. I don't know what mistake I do.
After you add dictionary1 to dictionary2, you are removing all of the objects.
This is the same dictionary that is in dictionary2 (it does not create a copy), therefore you are removing the objects from it as well.
You need to remove the line [dictionary1 removeAllObjects];.
Then, since you are done with dictionary1 at this point, you can either remove the reference to it or set to a nice new shiny dictionary which is ready to use:
// Remove the reference
dictionary1 = nil;
// Or, create a new, empty dictionary
dictionary1 = [[NSDictionary alloc] init];
Did you correctly alloc/init dictionary1 and dictionary2? Make sure the dictionaries themselves are not nil.
try this
dictionary1 = [dictionary2 objectForKey:[NSString stringWithFormat:#"%d",page]];
Related
I have an NSMutableArray of elements and I want to be able to conditionally set custom flags for some of the elements. For example an error count for certain elements if they return an error. If the count is more than 3, I would like to delete this element from an array.
What would be the best way to implement such behaviour?
A few options:
Have a separate array holding your counter for each object. When deleting one from your original array, remember to delete it's corresponding counter object.
Create a small class that contains an int value and whatever other object you are storing in the array, and populate your NSMutableArray with that object. You will then have your object and the error counter on the same place
Edit: The second option is the most scalable one, if you ever want to add more flags or whatever to it.
You would be better off creating a mutable array filled with mutable dictionaries. This would allow you have two keys corresponding to each index in the array:
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"some text, or what ever you want to store",#"body",
[NSNumber numberWithUnsignedInteger:0],#"errorCount",
nil];
[myMutableArray addObject:mutableDictionary];
And then here is a basic example of how to increment the error count for a specific item in the array:
- (void)errorInArray:(NSUInteger)idx
{
if ([[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue] == 2) {
[myMutableArray removeObjectAtIndex:idx];
}else{
NSUInteger temp = [[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue];
temp ++;
[[myMutableArray objectAtIndex:idx] setObject:[NSNumber numberWithUnsignedInteger:temp] forKey:#"errorCount"];
}
}
As alluded above, no need for custom object creation necessarily:
Creating a mutable array, creating a dictionary with objects/keys and adding said dictionary to the array:
NSMutableArray *myArray = [[NSMutableArray alloc] init] autorelease];
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"John Doe", #"elementName",
[NSNumber numberWithInt:0], #"errorCount",
nil];
[myArray addObject:myDictionary];
I have a NSMutableArray (tripHistory) that gets a NSMutableDictionary (currentUpdate) added to it every second with new data.
[currentUpdate setObject:testVariable forKey:#"Test"];
[tripHistory addObject:currentUpdate];
[currentUpdate removeAllObjects];
Yet when I loop through tripHistory calling [[tripHistory objectAtIndex:i] description] everything is null.
My loop is as follows:
for (int i=0; i<[tripHistory count]; i++)
{
NSLog(#"%#", [[tripHistory objectAtIndex:i] description]);
}
To initialize my variables, the following code is called only for the first update.
tripHistory = [[NSMutableArray alloc] init];
currentUpdate = [[NSMutableDictionary alloc] init];
Any ideas?
Adding an object to an NSArray doesn't make a copy of it. All that happens is its reference count is incremented. Therefore currentUpdate and the NSDictionary added to tripHistory are one-and-the-same. If you remove objects from currentUpdate you are also removing objects from the NSDictionary in tripHistory.
After adding currentUpdate to tripHistory all you need to do is release currentUpdate and start again with a new empty NSDictionary for the next update.
Do not remove objects. Dictionary and array are keeping references to the same objects. If you want to add another dict to array then release first dict and create a new one
You are removing all of the contents of the dictionary. Adding the dictionary to your array doesn't make a copy: The array will retain a reference to the dictionary, which you proceed to empty out each time you add something to it.
You should allocate a new dictionary each time through the loop and then add that dictionary o the array. If you are not using garbage collection or ARC you should also release the dictionary after it is added to the array.
In the Beginning iPhone 4 book, the author has this code to create a category for creating a deep copy of an NSDictionary that has an NSArray of names for each letter of the alphabet to show an example of an indexed table with a search bar.
#import "NSDictionary-MutableDeepCopy.h"
#implementation NSDictionary (MutableDeepCopy)
- (NSMutableDictionary *) mutableDeepCopy {
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys) {
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)]) oneCopy = [oneValue mutableDeepCopy];
else if ([oneValue respondsToSelector:#selector(mutableCopy)]) oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
oneCopy = [oneValue copy];
[returnDict setValue:oneCopy forKey:key];
[oneCopy release];
}
return returnDict;
}
#end
Can someone explain the for loop logic? I'm not sure what he's trying to do in seeing which value responds to which selector, and why it would be added to the dictionary. Thanks.
So, the for loop simply iterates through all the keys in the dictionary. Beforehand, we create a new dictionary called returnDict - this will be what we return.
For each key in the dictionary we want to copy, we...
Get the object stored for that key ([self valueForKey:key]), and save it into a variable called oneValue.
If oneValue implements our mutableDeepCopy method (ie, it's an NSDictionary) go call it, and assign the return value into a variable called oneCopy.
Else, we see if oneCopy implements the mutableCopy method. If it does, we put the output into the oneCopy variable.
At this point, we check to see if following steps (2) and (3) the oneCopy variable has had anything assigned to it (if (oneCopy == nil)). If it doesn't (ie, it's equal to nil) we can assume the object doesn't implement either mutableDeepCopy or mutableCopy, so we instead call a plain old copy and assign its value to oneCopy.
Add oneCopy into our returnDict dictionary using the original key.
That's the for loop - at the end of it all, we go and return the copied dictionary.
The logic in the for-loop is convoluted because the author is trying to get as mutable and as deep a copy of the entire array as possible. The code tries three different ways to satisfy this, in order of preference:
Use mutableDeepCopy if possible (if the object understands that message).
Otherwise, use mutableCopy if possible.
If all else fails, just use copy.
If the object is just plain not copiable, your code goes boom when it sends the object -copy, since no test is made for whether the object responds to -copy. This is appropriate, since trying to deep copy an array containing items that cannot be copied is definitely programmer error.
Ok so I want to create a temporary NSDictionary from a NSDictionary of nested dictionaries, but I want to deep copy individual items(dictionaries) from the top level dictionary.
The end result is to have a filtered dictionary that i can process and discard without effecting the main dictionary.
That sounds really confusing, so how about a little code to show you what I mean, heres the function i'm working on, this is a rough coding layout, but basically complete in its path of process.
I've looked at reference books and various samples online with no joy.
Cheers,
Darren
- (void)setPricingData
{
// get selected lens option
NSDictionary *aOption = [self.lensOptionsDict objectAtIndex:self._lensOptionsIndex];
if ( aOption == nil )
return;
// get selected lens type
NSDictionary *aType = [self.lensTypesDict objectAtIndex:self._lensTypesIndex];
if ( aType == nil )
return;
// get lens option id and variation_id
NSString *option_id = [aOption valueForKey:#"id"];
NSString *option_variation_id = [aOption valueForKey:#"variation_id"];
// create temp dictionary for type pricing selection
int count = [self.lensTypesDict count];
NSMutableDictionary *aPrices = [[NSMutableDictionary alloc] initWithCapacity:count];
// cycle prices for option id and variation_id matches
for ( NSDictionary *item in self.pricesDict )
{
NSString *variation_id = [item valueForKey:#"variation_id"];
NSString *value_id = [item valueForKey:#"value_id"];
// add matches to temp dictionary
if ( [option_variation_id isEqualToString: variation_id] )
{
if ( [option_id isEqualToString: value_id] )
[aPrices addObject: item];
}
}
// get price from temp dictionary for selected lens type index
NSDictionary *price = [aPrices objectAtIndex:self._lensTypesIndex];
if ( price != nil )
{
// assign values to outlet
self.priceAndStockId = [price valueForKey:#"price"];
self.priceSelected = [price valueForKey:#"price"];
}
// release temp dictionary
[aPrices release];
}
It looks like you're mixing up dictionaries with arrays.
Arrays respond to objectAtIndex whereas dictionaries respond to objectForKeys. Remember that an array is a set of cells that you can index into, starting from 0 all the way up to [array count] - 1.
A dictionary is similar to an array, except that a hash function is used as the indexing method. This means that you need a key to get, or set, and object.
Setting an object in an NSMutableDictionary
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
[myDictionary setObject:anObject forKey:aKey];
Or, you can have an array of keys and corresponding array of objects, and do:
NSDictionary *completeDictionary;
completeDictionary = [NSDictionary dictionaryWithObjects:objectArray
forkeys:keyArray count:[keyArray count]];
In either case, you must have keys for objects. This is in contrast to a regular array in which you can simply do
[myArray addObject:myObject];
To get objects from a dictionary, do
myObject = [myDictionary objectForKey:key];
To get objects from an array, do
myObject = [myArray objectAtIndex:anIntegerIndex];
Finally, your original question pertained to deep copying. To have your dictionary keep an object that won't change, ie, a deep copy, you can do the following:
Assuming I want to store a dictionary within a dictionary, and I have an associated key for the top-level dictionary, I can do the following:
I have an NSMutableDictionary, called topLevelDictionary
I have an NSDictionary, called dictionaryTwo
I have an NSString, which is my key, called myKey.
To make a deep copy of dictionaryTwo, I can do
// assuming topLevelDictionary is previously defined
[topLevelDictionary setObject:[[dictionaryTwo copy] autorelease] forKey:myKey];
In this manner topLevelDictionary will contain a copy of dictionaryTwo whereby if dictionaryTwo changes, the object in topLevelDictionary will not.
i have created NSMutableDictionary with 10 keys.Now i want to access NSMutableDictionary keys in a same order as it was added to NSMutableDictionary (using SetValue:* forKey:* );
How can i achieve that ?
If you absolutely must use a dictionary container, you have to use a key that is sortable by the order in which you add key-value pairs. Thus, when creating your dictionary, you use a key that is an auto-incrementing integer or similar. You can then sort on the (integer) keys and retrieve the values associated with those keys.
If you do all of that, however, you may as well just use an NSMutableArray and add values to the array directly! It will be much faster and require less code. You just retrieve objects in order:
for (id obj in myArray) { /* do stuff with obj... */ }
NSMutableDictionary can't do that. Take a look at e.g. Matt Gallaghers OrderedDictionary.
I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.
- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [referenceArray count]; i++)
{
if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
{
[returnArray addObject:[arrReference objectAtIndex:i]];
}
}
return [returnArray copy];
}
Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.
All that said, if you're trying to do something simple, it should work nicely. In your case, you could build up the reference array as you are looping through your setValue:forKey:.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (//whatever you are looping through here)
{
[yourDictionary setValue://whatever forKey:key];
[referenceArray addObject:key];
}
Then, when you want to loop over your items in the order they came in, you just
for (NSString *key in [self reorderArray:[myDict allKeys] toArray:referenceArray])
Actually you have a reference array in order manner then why you have to add to one more array.So i guess this approach is not good.Please consider my opinion.
Although #GenralMike 's answer works a breeze, it could be optimized by leaving off the unnecessary code as follows:
1) Keep an array to hold reference to the dictionary keys in the order they are added.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (id object in someArray) {
[yourDictionary setObject:object forKey:someKey];
[referenceArray addObject:someKey]; // add key to reference array
}
2) Now the "referenceArray" holds all of the keys in order, So you can retrieve objects from your dictionary in the same order as they were originally added to the dictionary.
for (NSString *key in referenceArray){
//get object from dictionary in order
id object = [yourDictionary objectForKey:key];
}