Getting NSDictionary keys sorted by their respective values - objective-c

I have an NSMutableDictionary with integer values, and I'd like to get an array of the keys, sorted ascending by their respective values. For example, with this dictionary:
mutableDict = {
"A" = 2,
"B" = 4,
"C" = 3,
"D" = 1,
}
I'd like to end up with the array ["D", "A", "C", "B"]. My real dictionary is much larger than just four items, of course.

The NSDictionary Method keysSortedByValueUsingComparator: should do the trick.
You just need a method returning an NSComparisonResult that compares the object's values.
Your Dictionary is
NSMutableDictionary * myDict;
And your Array is
NSArray *myArray;
myArray = [myDict keysSortedByValueUsingComparator: ^(id obj1, id obj2) {
if ([obj1 integerValue] > [obj2 integerValue]) {
return (NSComparisonResult)NSOrderedDescending;
}
if ([obj1 integerValue] < [obj2 integerValue]) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
Just use NSNumber objects instead of numeric constants.
BTW, this is taken from:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Collections/Articles/Dictionaries.html

NSDictionary has this neat method called allKeys.
If you want the array to be sorted though, keysSortedByValueUsingComparator: should do the trick.
Richard's solution also works but makes some extra calls you don't necessarily need:
// Assuming myDictionary was previously populated with NSNumber values.
NSArray *orderedKeys = [myDictionary keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2){
return [obj1 compare:obj2];
}];

Here's a solution:
NSDictionary *dictionary; // initialize dictionary
NSArray *sorted = [[dictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [[dictionary objectForKey:obj1] compare:[dictionary objectForKey:obj2]];
}];

The simplest solution:
[dictionary keysSortedByValueUsingSelector:#selector(compare:)]

Here i have done something like this:
NSMutableArray * weekDays = [[NSMutableArray alloc] initWithObjects:#"Sunday",#"Monday",#"Tuesday",#"Wednesday",#"Thursday",#"Friday",#"Saturday", nil];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSMutableArray *dictArray = [[NSMutableArray alloc] init];
for(int i = 0; i < [weekDays count]; i++)
{
dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:i],#"WeekDay",[weekDays objectAtIndex:i],#"Name",nil];
[dictArray addObject:dict];
}
NSLog(#"Before Sorting : %#",dictArray);
#try
{
//for using NSSortDescriptor
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"WeekDay" ascending:YES];
NSArray *descriptor = #[sortDescriptor];
NSArray *sortedArray = [dictArray sortedArrayUsingDescriptors:descriptor];
NSLog(#"After Sorting : %#",sortedArray);
//for using predicate
//here i want to sort the value against weekday but only for WeekDay<=5
int count=5;
NSPredicate *Predicate = [NSPredicate predicateWithFormat:#"WeekDay <=%d",count];
NSArray *results = [dictArray filteredArrayUsingPredicate:Predicate];
NSLog(#"After Sorting using predicate : %#",results);
}
#catch (NSException *exception)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sorting cant be done because of some error" message:[NSString stringWithFormat:#"%#",exception] delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert setTag:500];
[alert show];
[alert release];
}

Related

sort a dictionary by a key from an inner dictionary

i have a dictionary which i want to sort it according to a key from an inner dictionary. Each key in the super dictionary has a dictionary as value. As an example to illustrate what i'm talking about, here is a super dictionary with inner dictionaries respectively to each key.
{
key1 = {count = 2},
key2 = {count = 1}
}
thus the count key has to be the key used for the sorting. For now i know only how to sort arrays and i didn't encounter sorting dictionaries before. Any help will be appreciated.
get the array out of dictionary & sort it
NSArray* values = [myDict allValues];
NSArray* sortedValues = [values sortedArrayUsingSelector:#selector(comparator)];
another way to do it to make NSSortDescriptor
keyDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"count" ascending:YES] autorelease];
sortDescriptors = [NSArray arrayWithObject:keyDescriptor];
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
I pulled the code from Sort Descriptor Programming Topics. Also, Key-Value Coding comes into play, in that sortedArrayUsingDescriptors: will send a valueForKey: to each element in myArray, and then use standard comparators to sort the returned values.
Here's one way:
NSDictionary * dict1 = [[NSDictionary alloc]initWithObjectsAndKeys:[NSNumber numberWithInt:2], #"count", nil];
NSDictionary * dict2 = [[NSDictionary alloc]initWithObjectsAndKeys:[NSNumber numberWithInt:3], #"count", nil];
NSArray * arrayOfDictionaries = [[NSArray alloc]initWithObjects:dict1, dict2, nil];
NSArray * sortedArray = [arrayOfDictionaries sortedArrayUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
// are points equal?
if ([obj1[#"count"] intValue] != [obj2[#"count"] intValue]) {
// points not equal, compare points
if ([obj1[#"count"] intValue] > [obj2[#"count"] intValue])
return (NSComparisonResult)NSOrderedAscending;
else
return (NSComparisonResult)NSOrderedDescending;
}
else {
return (NSComparisonResult)NSOrderedSame;
}
}];
Let me know how it goes!
Okay, just to add to what others had posted (Thank you!) i also found my own solution which goes like this:
NSArray *keysByFrequency = [object keysSortedByValueUsingComparator:^NSComparisonResult(NSDictionary* obj1, NSDictionary* obj2) {
return [obj1[#"count"] compare:obj2[#"count"]];
}];
NSMutableArray *tags = [[NSMutableArray alloc] init];
for (int i = keysByFrequency.count-1; i >= 0; i--) {
[tags addObject:#{#"tag" : keysByFrequency[i], #"count" : object[keysByFrequency[i]][#"count"], #"type" : object[keysByFrequency[i]][#"type"]}];
}

Unable to sort NSMutableArray of Custom Objects

I've got an issue trying to sort an array of custom objects. It's looking as if my arrays aren't even hitting the sorting code, but rather simply just returning the array itself.
I have the following setup:
SearchResult : NSObject
--
Document : SearchResult
Tag : SearchResult
Folder : SearchResult
My code is getting returns as SearchResults then trying to compare them all with a key, name that is defined in the SearchResult implementation.
-(void) parseFolderContents:(NSDictionary *) data
{
NSMutableArray *searchResults = [[NSMutableArray alloc] init];
NSMutableArray *documents = [[NSMutableArray alloc] init];
NSMutableArray *folders = [[NSMutableArray alloc] init];
NSMutableArray *tags = [[NSMutableArray alloc] init];
NSArray *results = [data objectForKey:#"items"];
for (int i = 0; i < [results count]; i++)
{
SearchResult *result = (SearchResult *)[KTParser parseSearchResult:[results objectAtIndex:i]];
if ([result.type isEqualToString:#"document"]){
[documents addObject:result];
}
else if ([result.type isEqualToString:#"folder"])
{
[folders addObject:result];
}
else if ([result.type isEqualToString:#"tag"])
{
[tags addObject:result];
}
}
if ([documents count] > 0)
[searchResults addObject:documents];
if ([folders count] > 0)
[searchResults addObject:folders];
if ([tags count] > 0)
[searchResults addObject:tags];
....
So that's the code used to populate the array, which isn't anything special. I have tried each of these ways to compare the array. None have worked. Does anyone know where I'm going wrong?
First attempt:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedResults;
sortedResults = [searchResults sortedArrayUsingDescriptors:sortDescriptors];
Second attempt:
NSArray *sortedResults;
sortedResults = [searchResults sortedArrayUsingSelector:#selector(compare:)];
(implementing custom compare method on SearchResult/Document.m)
- (NSComparisonResult)compare:(SearchResult *)otherResult{
return [self.name compare:otherResult.name];
}
Third attempt:
sortedResults = (NSMutableArray *)[searchResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)]]];
Fourth attempt I tried using a block. I even tried putting code in to manipulate the sorting of it, which didn't work either. The array returned was exactly the same as the original:
sortedArray = [searchResults sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
if ([(SearchResult *)a itemId] < 20000 )
return NSOrderedAscending;
else
return NSOrderedDescending;
}];
Anyone have any ideas?
Found out the issue - I was adding arrays as objects instead of adding each object into the array. This was then calling sort on NSArray, instead of my custom objects. Changing the above code from [searchResults addObject:documents]; to [searchResults addObjectsFromArray:documents]; solved the issue!

Sort NSMutableArray with strings that contain numbers?

I have a NSMutableArray and it has the users high scores saved into it. I want to arrange the items numerically (the numbers are stored in NSStrings.)Example:4,2,7,8To2,4,7,8What is the simplest way to do this if the data is stored in NSStrings?
This code will do it:
//creating mutable array
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:#"4", #"2", #"7", #"8", nil];
//sorting
[myArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:(NSNumericSearch)];
}];
//logging
NSLog(#"%#", myArray);
It uses blocks, make sure your target OS supports that (It's 4.0 for iOS and 10.6 for OSX).
This code works. I tried it:
NSMutableArray *unsortedHighScores = [[NSMutableArray alloc] initWithObjects:#"4", #"2", #"7", #"8", nil];
NSMutableArray *intermediaryArray = [[NSMutableArray alloc] init];
for(NSString *score in unsortedHighScores){
NSNumber *scoreInt = [NSNumber numberWithInteger:[score integerValue]];
[intermediaryArray addObject:scoreInt];
}
NSArray *sortedHighScores = [intermediaryArray sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#", sortedHighScores);
The output is this:
2
4
7
8
If you have any questions about the code, just ask in the comments. Hope this helps!
The NSMutableArray method sortUsingSelector: should do it:
[scoreArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)]
should do it.
If the array is of nsdictionaries conaining numeric value for key number
isKeyAscending = isKeyAscending ? NO : YES;
[yourArray sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
NSString *str1 = [obj1 objectForKey:#"number"];
NSString *str2 = [obj2 objectForKey:#"number"];
if(isKeyAscending) { //ascending order
return [str1 compare:str2 options:(NSNumericSearch)];
} else { //descending order
return [str2 compare:str1 options:(NSNumericSearch)];
}
}];
//yourArray is now sorted
The answer from Darshit Shah make it smootly
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc]initWithKey:#"rank" ascending:YES selector:#selector(localizedStandardCompare:)];

NSSortDescriptor sort with number as string?

Got a an Array full of dictionary likes this:
(
{ order = "10";
name = "David"
};
{ order = "30";
name = "Jake";
};
{ order = "200";
name = "Michael";
};
)
When i'm using NSSortDescriptor like the code below it only sorts regarding to the first char so 200 is lower then 30. I can of course change the "order" object into a NSNumber instead of string and it would work. But is there a way to sort a string as int values without changing the source object?
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"norder" ascending:YES];
[departmentList sortUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]];
Update:
Thanks to bandejapaisa.
Here is the a working version for iOS 5 (Xcode where compalining).
NSArray *sortedArray;
sortedArray = [departmentList sortedArrayUsingComparator:(NSComparator)^(id a, id b) {
NSNumber *num1 = [NSNumber numberWithInt:[[a objectForKey:#"norder"] intValue]];
NSNumber *num2 = [NSNumber numberWithInt:[[b objectForKey:#"norder"] intValue]];
return [num1 compare:num2];
}];
departmentList = [sortedArray mutableCopy];
Using a NSNumber is overkill. You can save yourself a lot of overhead by doing the following:
NSArray *sortedArray = [someArray sortedArrayUsingComparator:^(id obj1, id obj2) {
return (NSComparisonResult) [obj1 intValue] - [obj2 intValue];
}];
Maybe sort using a comparator instead, or one of the other sorting methods:
NSArray *sortedArray = [someArray sortedArrayUsingComparator:^(id obj1, id obj2) {
NSNumber *num1 = [NSNumber numberWithInt:[obj1 intValue]];
NSNumber *num2 = [NSNumber numberWithInt:[obj2 intValue]];
return (NSComparisonResult)[rank1 compare:num2];
}];
All the above methods need good knowledge in basics to implement, but for the freshers i suggest the most simplest way is to use the native block method, hope this helps
NSArray* sortedArr =[fetchResults sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
int aValue = [[a valueForKey:#"subgroupId"] intValue];
int bValue = [[b valueForKey:#"subgroupId"] intValue];
return aValue>bValue; }];
Happy Coding...

Sorting array(NSArray) in descending order

I have a array of NSString objects which I have to sort by descending.
Since I did not find any API to sort the array in descending order I approached by following way.
I wrote a category for NSString as listed bellow.
- (NSComparisonResult)CompareDescending:(NSString *)aString
{
NSComparisonResult returnResult = NSOrderedSame;
returnResult = [self compare:aString];
if(NSOrderedAscending == returnResult)
returnResult = NSOrderedDescending;
else if(NSOrderedDescending == returnResult)
returnResult = NSOrderedAscending;
return returnResult;
}
Then I sorted the array using the statement
NSArray *sortedArray = [inFileTypes sortedArrayUsingSelector:#selector(CompareDescending:)];
Is this right solution? is there a better solution?
You can use NSSortDescriptor:
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:NO selector:#selector(localizedCompare:)];
NSArray* sortedArray = [inFileTypes sortedArrayUsingDescriptors:#[sortDescriptor]];
Here we use localizedCompare: to compare the strings, and pass NO to the ascending: option to sort in descending order.
or simplify your solution:
NSArray *temp = [[NSArray alloc] initWithObjects:#"b", #"c", #"5", #"d", #"85", nil];
NSArray *sortedArray = [temp sortedArrayUsingComparator:
^NSComparisonResult(id obj1, id obj2){
//descending order
return [obj2 compare:obj1];
//ascending order
return [obj1 compare:obj2];
}];
NSLog(#"%#", sortedArray);
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"length" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[wordsArray sortUsingDescriptors:sortDescriptors];
Using this code we can sort the array in descending order on the basis of length.