Objective-C : Sorting NSMutableArray containing NSMutableArrays - objective-c

I'm currently using NSMutableArrays in my developments to store some data taken from an HTTP Servlet.
Everything is fine since now I have to sort what is in my array.
This is what I do :
NSMutableArray *array = [[NSMutableArray arrayWithObjects:nil] retain];
[array addObject:[NSArray arrayWithObjects: "Label 1", 1, nil]];
[array addObject:[NSArray arrayWithObjects: "Label 2", 4, nil]];
[array addObject:[NSArray arrayWithObjects: "Label 3", 2, nil]];
[array addObject:[NSArray arrayWithObjects: "Label 4", 6, nil]];
[array addObject:[NSArray arrayWithObjects: "Label 5", 0, nil]];
First column contain a Label and 2nd one is a score I want the array to be sorted descending.
Is the way I am storing my data a good one ? Is there a better way to do this than using NSMutableArrays in NSMutableArray ?
I'm new to iPhone dev, I've seen some code about sorting but didn't feel good with that.
Thanks in advance for your answers !

This would be much easier if you were to create a custom object (or at least use an NSDictionary) to store the information, instead of using an array.
For example:
//ScoreRecord.h
#interface ScoreRecord : NSObject {
NSString * label;
NSUInteger score;
}
#property (nonatomic, retain) NSString * label;
#property (nonatomic) NSUInteger score;
#end
//ScoreRecord.m
#import "ScoreRecord.h"
#implementation ScoreRecord
#synthesize label, score;
- (void) dealloc {
[label release];
[super dealloc];
}
#end
//elsewhere:
NSMutableArray * scores = [[NSMutableArray alloc] init];
ScoreRecord * first = [[ScoreRecord alloc] init];
[first setLabel:#"Label 1"];
[first setScore:1];
[scores addObject:first];
[first release];
//...etc for the rest of your scores
Once you've populated your scores array, you can now do:
//the "key" is the *name* of the #property as a string. So you can also sort by #"label" if you'd like
NSSortDescriptor * sortByScore = [NSSortDescriptor sortDescriptorWithKey:#"score" ascending:YES];
[scores sortUsingDescriptors:[NSArray arrayWithObject:sortByScore]];
After this, your scores array will be sorted by the score ascending.

You don't need to create a custom class for something so trivial, it's a waste of code. You should use an array of NSDictionary's (dictionary in ObjC = hash in other languages).
Do it like this:
NSMutableArray * array = [NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObject:#"1" forKey:#"my_label"],
[NSDictionary dictionaryWithObject:#"2" forKey:#"my_label"],
[NSDictionary dictionaryWithObject:#"3" forKey:#"my_label"],
[NSDictionary dictionaryWithObject:#"4" forKey:#"my_label"],
[NSDictionary dictionaryWithObject:#"5" forKey:#"my_label"],
nil];
NSSortDescriptor * sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"my_label" ascending:YES] autorelease];
[array sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

Related

Sort NSMutableArray based on strings from another NSArray

I have an NSArray of strings that I want to use as my sort order:
NSArray *permissionTypes = [NSArray arrayWithObjects:#"Read", #"Write", #"Admin", nil];
I then have a NSMutableArray that may or may not have all three of those permissions types, but sometimes it will only be 2, sometimes 1, but I still want it sorted based on my permissionsTypes array.
NSMutableArray *order = [NSMutableArray arrayWithArray:[permissions allKeys]];
How can I always sort my order array correctly based on my using the permissionTypes array as a key?
I would go about this by creating a struct or an object to hold the permission types.
Then you can have...
PermissionType
--------------
Name: Read
Order: 1
PermissionType
--------------
Name: Write
Order: 2
and so on.
Then you only need the actual array of these objects and you can sort by the order value.
[array sortUsingComparator:^NSComparisonResult(PermissionType *obj1, PermissionType *obj2) {
return [obj1.order compare:obj2.order];
}];
This will order the array by the order field.
NSMutableArray *sortDescriptors = [NSMutableArray array];
for (NSString *type in permissionTypes) {
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:type ascending:YES] autorelease];
[sortDescriptors addObject:descriptor];
}
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
Use whichever sorting method on NSMutableArray you prefer, you will either provide a block or a selector to use for comparing two elements. In that block/selector rather than comparing the two strings passed in directly look each up in your permissionTypes array using indexOfObject: and compare the resulting index values returned.
I suggest you another approuch:
- (void)viewDidLoad
{
[super viewDidLoad];
arrayPermissions = [[NSMutableArray alloc] init];
NSDictionary *dicRead = [NSDictionary dictionaryWithObjectsAndKeys:
#"Read", #"Permission", nil];
NSDictionary *dicWrite = [NSDictionary dictionaryWithObjectsAndKeys:
#"Write", #"Permission", nil];
NSDictionary *dicAdmin = [NSDictionary dictionaryWithObjectsAndKeys:
#"Admin", #"Permission", nil];
NSLog(#"my dicRead = %#", dicRead);
NSLog(#"my dicWrite = %#", dicWrite);
NSLog(#"my dicAdmin = %#", dicAdmin);
[arrayPermissions addObject:dicRead];
[arrayPermissions addObject:dicWrite];
[arrayPermissions addObject:dicAdmin];
NSLog(#"arrayPermissions is: %#", arrayPermissions);
// create a temporary Dict again
NSDictionary *temp =[[NSDictionary alloc]
initWithObjectsAndKeys: arrayPermissions, #"Permission", nil];
// declare one dictionary in header class for global use and called "filteredDict"
self.filteredDict = temp;
self.sortedKeys =[[self.filteredDict allKeys]
sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sortedKeys is: %i", sortedKeys.count);
NSLog(#"sortedKeys is: %#", sortedKeys);
}
hope help

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:)];

Sorting and matching

NSMutableArray *full_text_list = [[NSMutableArray alloc]init];
[full_text_list addObject:#"for"];
[full_text_list addObject:#"for your information"];
[full_text_list addObject:#"you"];
[full_text_list addObject:#"at"];
NSMutableArray *short_text_list = [[NSMutableArray alloc]init];
[short_text_list addObject:#"4"];
[short_text_list addObject:#"fyi"];
[short_text_list addObject:#"u"];
[short_text_list addObject:#"#"];
i dont want to sort the second array. i want to get the appropriate element based on index.
I want to sort only the full_text_list array based on length, so i tried a below
NSSortDescriptor * descriptors = [[[NSSortDescriptor alloc] initWithKey:#"length"
ascending:NO] autorelease];
NSArray * sortedArray = [full_text_list sortedArrayUsingDescriptors:
[NSArray arrayWithObject:descriptors]];
and the above code works fine.But i am not sure how to match short_text_list array with new sorted array
So when doing like [full_text_list objectatindex:0] and [short_text_list objectatindex:0] will not match
result would be "for your information" and "for" but the result should be "for your information" and "fyi"
Please let me know
How should it match? You have two arrays and are just sorting one and expect the second one automagically gets sorted too? This can not work. Why don't you just build a dictionary with the long information as key and the short one as value or vs?
A second way to do this would be:
// Create your two arrays and then combine them into one dictionary:
NSDictionary *textDict = [NSDictionary dictionaryWithObjects:short_text_list
forKeys:full_text_list];
// Create your sorted array like you did before:
NSSortDescriptor * descriptors = [[[NSSortDescriptor alloc] initWithKey:#"length" ascending:NO] autorelease];
NSArray * sortedArray = [full_text_list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptors]];
// Then to access short_text, you would use:
NSString *object0ShortText = [textDict objectForKey:[sortedArray objectAtIndex:0]];
I would create a new class which contains both values and insert that into the array instead of creating the two separate array's in the first place:
#interface TextList : NSManagedObject
#property (nonatomic, retain) NSString *full_text;
#property (nonatomic, retain) NSString *short_text;
- (TextList *)initWithFullText:(NSString *)full_text shortText:(NSString *)short_text;
#end
Create your .m file, and then when you want to use it, use something like:
NSMutableArray *full_text_list = [[NSMutableArray alloc]init];
[full_text_list addObject:[TextList initWithFullText:#"for" shortText:#"4"]];
[full_text_list addObject:[TextList initWithFullText:#"for your information" shortText:#"fyi"]];
[full_text_list addObject:[TextList initWithFullText:#"you" shortText:#"u"]];
[full_text_list addObject:[TextList initWithFullText:#"at" shortText:#"#"]];
Then perform the sort:
NSSortDescriptor * descriptors = [[[NSSortDescriptor alloc] initWithKey:#"full_text.length" ascending:NO] autorelease];
NSArray * sortedArray = [full_text_list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptors]];
Now you can do [[sortedArray objectAtIndex:0] full_text]; and [[sortedArray objectAtIndex:0] short_text]; or
TextList *txtList = [sortedArray objectAtIndex:0];
// txtList.full_text and txtList.short_text are both valid.

How do I alphabetically sort a custom object field within a NSMutable Array?

I have a custom object like:
#import <Foundation/Foundation.h>
#interface Store : NSObject{
NSString *name;
NSString *address;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *address;
#end
I have an array of NSMutableArray (storeArray) containing Store objects:
store1 = [[Store alloc] init];
store1.name = #"Walmart";
store1.address = #"walmart address here..";
store2 = [[Store alloc] init];
store2.name = #"Target";
store2.address = #"Target address here..";
store3 = [[Store alloc] init];
store3.name = #"Apple Store";
store3.address = #"Apple store address here..";
//add stores to array
storeArray = [[NSMutableArray alloc] init];
[storeArray addObject:store1];
[storeArray addObject:store2];
[storeArray addObject:store3];
My question is how can I sort the array by the store name? I know I can sort an array alphabetically by using this line:
[nameOfArray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
How can I apply this to the store name of my Store class?
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(caseInsensitiveCompare:)];
[nameOfArray sortedArrayUsingDescriptors:#[sortDescriptor]];
Related documentation:
[NSArray sortedArrayUsingDescriptors:]
NSSortDescriptor
Regexident's answer is based on NSArrays, the corresponding in-place sorting for NSMutableArray would be -sortUsingDescriptors:
[storeArray sortUsingDescriptors:
[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(caseInsensitiveCompare:)]]];
Now storeArray it-self will be sorted.

How can I retrieve all the contents of an NSDictionary?

I want to select and retrieve all the contents from an NSDictionary. I have a structure like this
- (void)viewDidLoad {
[super viewDidLoad];
listaOggetti = [[NSMutableArray alloc] init];
NSArray *arrayOne = [NSArray arrayWithObjects: #"First",#"Second",#"Third", nil];
NSArray *sortedOne = [arrayOne sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictOne = [NSDictionary dictionaryWithObject:sortedOne forKey:#"Elementi"];
NSArray *arrayTWo = [NSArray arrayWithObjects:#"First1",#"Second1" ..., nil];
NSArray *sortedTwo = [arrayTwo sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictTwo = [NSDictionary dictionaryWithObject:sortedTWo forKey:#"Elementi"];
NSArray *arrayThree = [NSArray arrayWithObjects:#"First2",#"Second2" ... , nil];
NSArray *sortedThree = [arrayThree sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictThree = [NSDictionary dictionaryWithObject:sortedThree forKey:#"Elementi"];
[listaOggetti addObject:dictOne];
[listaOggetti addObject:dictTwo];
[listaOggetti addObject:dictThree];
}
And I want to retrieve all the objects for the key #"Elementi" (should be around 45) in order to add them in another array, like:
NSDictionary *dict = [listaOggetti objectAtIndex:indexPath.section];
NSArray *array = [dict objectForKey:#"Elementi"];
cellValue = [array objectAtIndex:indexPath.row] ;
(With this, dict is only 9 objects filled in my project).
At the end, the *array should be around 45 objects filled. I tried with allValues, but didn't work.
How can I fix it?
The easiest is to do this in -viewDidLoad:
NSMutableArray *allObjects = [[NSMutableArray alloc] init];
[allObjects addObjectsFromArray:sortedOne];
[allObjects addObjectsFromArray:sortedTwo];
[allObjects addObjectsFromArray:sortedThree];
Alternately, you can get them from the dictionaries in a similar fashion:
NSMutableArray *allObjects = [[NSMutableArray alloc] init];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:0] objectForKey#"Elementi"];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:1] objectForKey#"Elementi"];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:2] objectForKey#"Elementi"];
What you are failing to understand is that listaOggetti is an NSMutableArray containing three objects. When you call
NSDictionary *dict = [listaOggetti objectAtIndex:indexPath.section];
the result is that dict is a single dictionary, one of the three objects in listaOggetti. Therefore when you call
NSArray *array = [dict objectForKey:#"Elementi"];
the result is that array is the object for the key #"Elementi" of that one single dictionary dict. Your code makes no attempt to combine the three DIFFERENT dictionaries or to combine the three arrays, each set as objectForKey:#"Elementi" for the three DIFFERENT dictionaries.
If you want one array that is the concatenation of all three different arrays, then use one of the snippets provided above. In both of these snippets, the result is that allObjects is an NSMutableArray containing all three arrays, in order.