I am having an NSMutableDictionary with, being filled dynamically. It looks like this:
{
SomeKey = 6;
AnotherKey = 2;
JustAnotherKey = 28;
}
I need to sort this, so it will be like this one:
{
JustAnotherKey = 28;
SomeKey = 6;
AnotherKey = 2;
}
Is there any way to achieve this? Thanks in advance.
No, sorry!
An NSDictionary doesn't support sorting it's keys - you would have to do that yourself.
Get the keys array from your dictionary, sort that and then go through and get the values from your dictionary. Something like :
NSArray *keys = [myDictionary allKeys];
NSArray *sortedKeys = [keys sortedArrayUsingSelector:#selector(compare:)];
NSArray *values = [NSMutableArray array];
for (id key in sortedKeys)
[values addObject:[myDictionary objectforKey:key]];
Now, values are in the correct order.
However, that's quite a lot of work; if you want them sorted, I would look at storing them in an array to start with?
A Dictionary is an unordered set, which means it doesn't have any order of its elements.So even if you insert the first object as say "one":"first value", and then "two":"second value", when you iterate over the keys, you might get it in any random order(eg: "two' and then "one").
However, if all you want is the values in sorted order, you can iterate over all the keys, fetch the values and store it in an array, and then sort them.
NSArray *values=[myDict allValues];
NSMutableArray *sortedKeys=[[NSMutableArray alloc] init];
NSArray *sortedValues = [values sortedArrayUsingSelector:#selector(yourSelector)];
for (val in sortedArray){
NSString *key=(NSString*)[[myDict allKeysForObject:val] objectAtIndex:0];
[sortedKeys addObject:key]
}
This would be starter for getting first the values in sorted order, and then the corresponding keys. (It is not doing any error checks. So beware of OutOfIndex exceptions).
I am not sure how good would be the efficiency of this code be, coz allKeysForObject would be iterating over all the keys.
Instead of using a NSMutableDictionary, you might consider to use a NSMutableArray and populate it with your own model class which contains a property with the key string and a property with the value.
NSMutableArray provides methods to sort these objects.
Is this helpful enough?
Related
I am having a problem that I think I am overcomplicating.
I need to make either an NSMutableArray or NSMutableDictionary. I am going to be adding at least two objects like below:
NSMutableArray *results = [[NSMutableArray alloc] init];
[results addObject: [[NSMutableArray alloc] initWithObjects: [NSNumber numberWithInteger:myValue01], #"valueLabel01", nil]];
This gives me the array I need but after all the objects are added I need to be able to sort the array by the first column (the integers - myValues). I know how to sort when there is a key, but I am not sure how to add a key or if there is another way to sort the array.
I may be adding more objects to the array later on.
Quick reference to another great answer for this question:
How to sort NSMutableArray using sortedArrayUsingDescriptors?
NSSortDescriptors can be your best friend in these situations :)
What you have done here is create a list with two elements: [NSNumber numberWithInteger:myValue01] and #"valueLabel01". It seems to me that you wanted to keep records, each with a number and a string? You should first make a class that will contain the number and the string, and then think about sorting.
Doesn't the sortedArrayUsingComparator: method work for you? Something like:
- (NSArray *)sortedArray {
return [results sortedArrayUsingComparator:(NSComparator)^(id obj1, id obj2)
{
NSNumber *number1 = [obj1 objectAtIndex:0];
NSNumber *number2 = [obj2 objectAtIndex:0];
return [number1 compare:number2]; }];
}
I'm reading a JSON code such as:
[{"question_id":"1",
"trigger_value":"Yes",
"question_text":"What is your first name?",
"question_type":"YN"},
{"question_id":"2",
"trigger_value":"Yes",
"question_text":"What is your second name?",
"question_type":"YN"}
]
But once it's set into NSMutableArray, the duplicate values are deleted. I would like to allow them to check the question_type for each question.
NSString *question_id;
NSString *question_text;
NSString *question_type;
while (dicjson = (NSDictionary*)[enumerator nextObject]) {
question_id = [dicjson objectForKey:#"question_id"];
question_type = [dicjson objectForKey:#"question_type"];
[mutjson setObject:question_id forKey:question_type];
}
Would you give me any idea of allowing duplicate values...?
Thanks in advance.
mutjson looks like a mutable dictionary and not a mutable Array.
So yes, in a dictionary object if you are setting the same key, it will overwrite the previous value.
If you need to store dictionary object, create a mutable array and add each object inside that array as a dintionary object...
NSMutableArray *results = [[NSMutableArray alloc] init];
while (dicjson = (NSDictionary*)[enumerator nextObject]) {
question_id = [dicjson objectForKey:#"question_id"];
question_type = [dicjson objectForKey:#"question_type"];
[result addObject:[NSDictionary dictionaryWithObject:question_id forKey:question_type]];
}
You cannot setObject:forKey: in NSMutableArray. You have to use addObject:. Its also much easier to create the array like this:
NSArray *values = [jsonDict allValues];
You are confusing an array for a dictionary. An array can hold duplicate values. A dictionary cannot hold duplicate keys.
The JSON response is an array of dictionaries. The way you've written your code, specifically [mutjson setObject:question_id forKey:question_type]; seems to suggest that you are simply using a dictionary.
If you would like to check the question type for each question, try instead:
NSString *question_type;
for (NSMutableDictionary *dict in dicjson) {
// I would suggest renaming dicjson to something more descriptive, like results
question_type = [dict objectForKey: #"question_type"];
// Do what you would like now that you know the type
}
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];
}
I have an array of NSDictionaries. How can I pull out the first element in the dictionary?
NSArray *messages = [[results objectForKey:#"messages"] valueForKey:#"message"];
for (NSDictionary *message in messages)
{
STObject *mySTObject = [[STObject alloc] init];
mySTObject.stID = [message valueForKey:#"id"];
stID = mySTObject.stID;
}
There is no "first" element in an NSDictionary; its members have no guaranteed order. If you just want one object from a dictionary, but don't care which key it's associated with, you can do:
id val = nil;
NSArray *values = [yourDict allValues];
if ([values count] != 0)
val = [values objectAtIndex:0];
NSDictionaries are unordered, meaning that there are not first or last element. In fact, the order of the keys are never guaranteed to be the same, even in the lifetime of a specific dictionary.
If you want any object, you can get one of the keys:
id key = [[message allKeys] objectAtIndex:0]; // Assumes 'message' is not empty
id object = [message objectForKey:key];
NSArray has a selector named firstObject that simplifies the code and makes it more readable:
id val = [[yourDict allValues] firstObject];
If yourDict is empty val will be nil, so is not necessary to check the dictionary/array size.
Simplest:
[[dict objectEnumerator] nextObject];
According to Apple, calls to allKeys or allValues incur the cost of creating new arrays:
A new array containing the dictionary’s values, or an empty array if
the dictionary has no entries (read-only)
So, an alternative option that does not incur such cost could look like this:
NSString* key = nil;
for(key in yourDict)
{ // this loop will not execute if the dictionary is empty
break; // exit loop as soon as we enter it (key will be set to some key)
}
id object = yourDict[key]; // get object associated with key. nil if key doesn't exist.
Note: If the dictionary is empty, the key will remain nil, and the object returned will also be nil, we therefore don't need special handling of the case where the dictionary is actually empty.
If someone is still looking for answer for this type of situation then can refer this:
// dict is NSDictionary
// [dict allKeys] will give all the keys in dict present
// [[dict allKeys]objectAtIndex:0] will give from all the keys object at index 0 because [dict allKeys] returns an array.
[[dict allKeys]objectAtIndex:0];
If you have NSDictionary named message,
It's pretty simple:
message[[[message allKeys] objectAtIndex:0]];
But you have to be sure (or better check) that your dictionary has at least one element.
Here is how you can check it:
if ([message allKeys] > 0) NSLog(#"%#", message[[[message allKeys] objectAtIndex:0]]);
But NSDictionary has no guaranteed order, so you probably should use this code only if your dictionary has only one element.
[UPDATE]
It's also good idea to use this if you need to get ANY element of dictionary
Try this:
NSDictionary *firstValue = [responseObject objectAtIndex:0];