Sorting the Key/Values stored in NSArray - ios

I have a
NSDictionary* dict = [[NSDictionary alloc]initWithObjectsAndKeys::arrayOne,#"Plants",arrayTwo,#"Animals"),arrayThree,#"Birds",nil];`
self.displayArray =[[dict allKeys] sortedArrayUsingSelector:#selector(compare:)];
Everything works fine, I am able to see all the key value/pair in the table but they are in sorted order. i.e Animals,Birds,Plants.
But I want to display as Plants,Animals,Birds.
Can anyone tell me how to sort the array in my customized order?
I have googled and found that we can use NSSortDescriptor for sorting. But I am not very clear with that. Can anyone help me ?

As your ordering doesnt follow any natural order, a simple solution could be to keep track of the order with another array
NSArray *array1 = [NSArray arrayWithObjects:#"rose",#"orchid",#"sunflower",nil];
NSArray *array2 = [NSArray arrayWithObjects:#"dog", #"cat",#"ogre",#"wookie", nil];
NSArray *array3 = [NSArray arrayWithObjects:#"parrot",#"canary bird",#"tweety",#"bibo",nil];
NSArray *keys = [NSArray arrayWithObjects:#"Plants",#"Animals",#"Birds", nil];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
array1,[keys objectAtIndex:0],
array2,[keys objectAtIndex:1],
array3,[keys objectAtIndex:2],
nil];
for (NSString *key in keys) {
NSLog(#"%# %#", key, [dict objectForKey:key]);
}
Matt shows in his fantastic blog, how to create a ordered dictionary, that essentially uses another array to keep the order just as I showed here: OrderedDictionary: Subclassing a Cocoa class cluster

You're on the right track, Cyril.
Here is some Apple documentation on "Creating and using Sort Descriptors"
Basically you need to subclass NSSortDescriptor and in your subclass, implement your own "compare:" method (you can actually name it anything you want; it needs to return a "NSComparisonResult") that somehow logically returns "Plants" before "Animals".

Related

NSArray sortedArrayUsingDescriptors: New Copy or Retain?

According to NSArray class reference there are 4 type of methods to sort array:
1- sortedArrayUsingComparator:
2- sortedArrayUsingSelector:
3- sortedArrayUsingFunction:context:
4- sortedArrayUsingDescriptors:
For first three methods it mentioned :
The new array contains references to the receiving array’s elements, not copies of them.
But for the forth method (descriptor) it mentioned:
A copy of the receiving array sorted as specified by sortDescriptors.
But following example shows like the other 3 methods, descriptor also retain original array and do not return a new copy of it:
NSString *last = #"lastName";
NSString *first = #"firstName";
NSMutableArray *array = [NSMutableArray array];
NSDictionary *dict;
NSMutableString *FN1= [NSMutableString stringWithFormat:#"Joe"];
NSMutableString *LN1= [NSMutableString stringWithFormat:#"Smith"];
NSMutableString *FN2= [NSMutableString stringWithFormat:#"Robert"];
NSMutableString *LN2= [NSMutableString stringWithFormat:#"Jones"];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN1, first, LN1, last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN2, first, LN2, last, nil];
[array addObject:dict];
// array[0].first = "Joe" , array[0].last = "Smith"
// array[1].first = "Robert" , array[1].last = "Jones"
NSSortDescriptor *lastDescriptor =[[NSSortDescriptor alloc] initWithKey:last
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =[[NSSortDescriptor alloc] initWithKey:first
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:descriptors];
// array[1] == sortedArray[0] == ("Robert" , "Jones")
// comparing array entries whether they are same or not:
NSLog(#" %p , %p " , [array objectAtIndex:1] , [sortedArray objectAtIndex:0] );
// 0x10010c520 , 0x10010c520
it shows objects in both arrays are same,
"A copy of the receiving array sorted as specified by sortDescriptors" means that the array object is copied not the elements in the array. The reason the documentation uses the word "copy" is to make it clear that the returned array is not the same array instance as the receiver.
Elements in an array are never copied in Cocoa with the exception of initWithArray:copyItems:YES which will copy the first level items in the original array to the new array. Even then, this copy is done by calling copyWithZone: on the elements, so caveats apply depending on what elements are in your array.
Note that Cocoa is reference counted, so the concept of "deep copies" is not inherently built in for a reason. This is also (in part) the reason why array objects in cocoa come in two flavors (NSArray and NSMutableArray) and are usually immutable (NSArray) instead of as in other languages where there is not usually a concept of immutable and mutable arrays.
see this SO answer for how to get a "deep copy" of an NSArray.

sort NSDictionary based on single object key

I would like some help sorting an NSArray of NSDictionary values based on each objects ISV key.
This is the code I have so far for creating my array objects so you have a better idea of what I am trying to do.
NSArray *combinedKeysArray = [NSArray arrayWithObjects:#"HASM", #"ISL", #"ISV", nil];
valuesCombinedMutableArray = [NSMutableArray arrayWithObjects:[dict objectForKey:#"HASM"],
[dict objectForKey:#"ISL"],
[dict objectForKey:#"ISV"],
nil];
combinedDictionary = [NSDictionary dictionaryWithObjects:valuesCombinedMutableArray
forKeys:combinedKeysArray];
[unSortedrray addObject:combinedDictionary];
// how do I then sort unSortedArray by the string values in each object ISV key?
any help would be greatly appreciated.
This can solve your problem
How to sort an NSMutableArray with custom objects in it?
https://stackoverflow.com/a/805589/1294448
You can use NSSortDescriptor to sort NSArays
Then in NSArray you have a method called sortedArrayUsingDescriptors
Or NSComparisonResult ca also be helpful some time http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/doc/uid/20000138-BABCEEJD
you won't be able to sort unSortedArray because it will only have one element in it (ie in your last line of code you are adding a single object by addObject).
That said, you cannot sort the dictionary either.. b/c dictionaries are unsorted by definition.
you can iterate over the keys of the dictionary in a specific order though, you can sort an array containing the keys of the dictionary.
NSArray *keys = [theDictionary allKeys];
NSArray *sortedKeys = [keys sortedArrayUsingSelector:#selector(compareMethod:)];
You can use -sortedArrayUsingComparator: to sort any way you need.
[unSortedrray sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *dict1, NSDictionary *dict2) {
return [[dict1 objectForKey:#"ISV"] localizedCompare:[dict2 objectForKey:#"ISV"]];
}];

NSDictionary valueForKey crash

How can i retrieve the key value from the below NSMutableArray array. The below code crashes on isEqualToString. However i can see the value of nsRet in the variable view window as #\x18\xaa\x01\xc8\a before running that statement.
NSMutableArray* nsMyList = [[NSMutableArray alloc] init];
[nsMyList addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"valueOfKey", #"Key",
nil]];
NSString *nsRet = [nsMyList valueForKey:#"Key"];
if ([nsRet isEqualToString:#"deviceClass"])
{
NSLog(#"Key value:%#", nsRet);
}
Can anyone here please help me get the correct value for the key?
Thanks.
This is because you need objectForKey:, not valueForKey:. The valueForKey: method is for key-value programming. Moreover, the call should be on the [nsMyList objectAtIndex:0], like this:
NSString *nsRet = [[nsMyList objectAtIndex:0] objectForKey:#"Key"]
You've stored the NSDictionary in an array. The correct access based on your code would be:
NSDictionary *dict = [nsMyList objectAtIndex:0];
nsret = [dict valueForKey:#"Key"];
It looks like you are trying to get the valueForKey: on an NSMutableArray rather than on the dictionary.
What you want is:
[[nsMyList objectAtIndex:0] valueForKey:#"Key"];
I am a bit lost.
In order to access the dictionary you just create you need to obtain the first element in the NSMutableArray and then the dictionary.
It will be something like this:
NSString *nsRet = [nsMyList[0] objectForKey:#"Key"]
I think it can solve it.

Sort NSDictionary by value within an inner nested NSDictionary

I'm working on a language learning app. So I have an NSMutableDictionary with 'word' as keys. The objects for these keys are nested NSDictionaries with the keys 'frequency' and 'count'. NSNumbers are the objects for 'frequency' and 'count'.
Here is the initializing code:
NSString* path = [[NSBundle mainBundle] pathForResource:#"french_top_50000"
ofType:#"txt"];
NSString *fh = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
self.userWordlist = [[NSMutableDictionary alloc] init];
for (NSString *word in fh) {
NSArray *keyArray = [[NSArray alloc] initWithObjects:#"frequency", #"count", nil];
NSArray *objectArray = [[NSArray alloc] initWithObjects:frequency, count, nil];
NSDictionary *detailsDict = [[NSDictionary alloc] initWithObjects:objectArray forKeys:keyArray];
[self.userWordlist setObject:detailsDict forKey:word];
}
I'm displaying part of this list in a table, and I want to sort by 'frequency', one of the inner keys. I can't figure out how to do this.
In case the first thought is, "Why did you store this in a nested dictionary?", I wanted the words to be keys because in other parts of the app I frequently search to see if a word is in the NSMutableDictionary.
I thought about having a flat dictionary with the following keys:
'word','frequency','count'
... but I'd have to enumerate to check for inclusion of words.
If there are any suggestions for a better data structure strategy I'd love to hear them. I'm going to be checking very frequently for inclusion of 'words' and less frequently will be sorting based on 'frequency' or 'count'.
I've seen lots of question similar to this but they're all for flat dictionaries.
If I understand correctly, use keysSortedByValueUsingComparator: like this:
NSArray *keysByFrequency = [self.userWordlist keysSortedByValueUsingComparator:^NSComparisonResult(NSDictionary* obj1, NSDictionary* obj2) {
return [obj1[#"frequency"] compare:obj2[#"frequency"]];
}];
Then you can iterate keys sorted by their frequency
for (NSString *word in keysByFrequency){
NSDictionary *detailsDict = self.userWordList[word];
// Do whatever...
}

can NSPredicates be used to replace objects in an array with values from a dictionary?

If I had an NSDictionary like this:
NSMutableDictionary *valuesDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:-60.0],#”a”,
[NSNumber numberWithDouble:0.0],#”b”,
[NSNumber numberWithDouble:-12.0],#”c”,
[NSNumber numberWithDouble:.3],#”x”, nil];
and an array like this:
NSArray *program = [NSArray arrayWithObjects: #"a",#"12.6",#"100",#"+",#"x",nil];
what would the code look like to return an array programWithVariableValues, for example, consisting of #”-60″,#”12.6″,#”100″,#"+",#”.3″,nil?
(replacing any variables found in the array with their valueforkey’s)
Would that be a good place to utilize NSPredicate? or just fast enumeration? or something else?
There may be a clever one-liner solution using predicates and/or valueForKeyPath operators, but until somebody figures that one out, this should do the trick:
NSMutableArray *programWithVariableValues = [NSMutableArray array];
for (NSString *key in program)
{
[programWithVariableValues addObject:[[valuesDictionary objectForKey:key] description] ?: key];
}
The programWithVariableValues array now contains your values (as strings). If you'd prefer to keep the numbers as NSNumbers, take out the "[... description]" call.

Resources