How to sort a mutablensarray? - xcode

I made this code to sort an nsmutablearray but how to send back an NSMutablearray ? i need this because a add information later in my mutablearray.
-(NSArray*)trierTableau:(NSMutableArray*)ptableau champsFiltre:(NSString*) champs{
NSSortDescriptor *lastDescriptor =
[[[NSSortDescriptor alloc]
initWithKey:champs
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray * descriptors = [NSArray arrayWithObjects:lastDescriptor, nil];
NSArray * sortedArray = [ptableau sortedArrayUsingDescriptors:descriptors];
return sortedArray;
}

If you want to sort your NSMutableArray in-place, then you could call sortUsingDescriptors: instead of sortedArrayUsingDescriptors:, which returns a sorted copy of your array. sortUsingDescriptors: will sort the existing NSMutableArray.
e.g.
-(void)trierTableau:(NSMutableArray*)ptableau champsFiltre:(NSString*) champs {
NSSortDescriptor *lastDescriptor =
[[[NSSortDescriptor alloc]
initWithKey:champs
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray * descriptors = [NSArray arrayWithObjects:lastDescriptor, nil];
[ptableau sortUsingDescriptors:descriptors];
}
This way you don't have to return the NSMutableArray either.
If you actually want a sorted copy of the array, then you'll need to create a new NSMutableArray, use addObjectsFromArray: to copy the elements over, and then use sortUsingDescriptors: on the new array. But I think that sorting the existing NSMutableArray is probably what you'll want.

Related

Building up an array an element at a time

Currently I'm populating an array in one line, e.g.
self.monthMonths = [[NSArray alloc] initWithObjects:#"January", #"February", #"March", #"April", #"May", #"June",#"July",#"August",#"September",#"October",#"November",#"December", nil];
What is the syntax to add these elements one at a time as I want to pull the data from a database. I'm using the months of the year as an example.
while([results next]) {
NSString *months = [results stringForColumn:#"month"];
self.month = [[NSArray alloc] initWithObjects:#"month",nil];
//[NSArray
NSLog(#"Month: %#",month);
}
Create an NSMutableArray and add the objects to it one by one with addObject
You need to use NSMutableArray instead, and call -addObject:

Sort a NSMutableArray in objective-c?

I am working on NSMutable Array containing list of "First Name" in it.I want to sort it alphabatically.
I tried this code
NSSortDescriptor * sortFriend = [[[NSSortDescriptor alloc] initWithKey:kfirstName ascending:YES] autorelease];
NSArray * descriptors = [NSArray arrayWithObject:sortFriend];
NSArray * sorted = [Friendsarr sortedArrayUsingDescriptors:descriptors];
correct me if i am wrong.I do have an array of names also if that could be used.
Thanks
I tried the code
NSSortDescriptor * sortFriend = [[[NSSortDescriptor alloc] initWithKey:kfirstName ascending:YES selector: #selector(caseInsensitiveCompare:)] autorelease];
NSArray * descriptors = [NSArray arrayWithObject:sortFriend];
NSArray * arrmp = [temparray sortedArrayUsingDescriptors:descriptors];
But still my result contain 2 'k' chars. one 'k' and 'K'.I want to have case insensitive result
If your NSMutableArray only contains objects of type NSString, simply do:
[myMutableArray sortUsingSelector:#selector(compare:)];
You can sort NSMutable array case insensitively by this code
NSSortDescriptor *sorter=[[[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:#selector(caseInsensitiveCompare:)]autorelease];
NSArray *sortdescriptor=[NSArray arrayWithObject:sorter];
[cpy_arr_Service_Name sortUsingDescriptors:sortdescriptor];
I found the solution of sorting NSMutableArrays with sample code which can be found on the site below.
http://www.icodeblog.com/2010/12/10/implementing-uitableview-sections-from-an-nsarray-of-nsdictionary-objects/
I hope it help others to as it provides indexed sorting of a grouped table view using an array as the datasource

Sort a NSMutuableArray

I have a NSMutableArray that is loaded with a inforamtion from a dictionary...
[self.data removeAllObjects];
NSMutableDictionary *rows = [[NSMutableDictionary alloc] initWithDictionary:[acacheDB.myDataset getRowsForTable:#"sites"]];
self.data = [[NSMutableArray alloc] initWithArray:[rows allValues]];
There are two key value pairs in the rows dictionary.
I need to sort the self.data NSMutableArray in alphabetical order.
How is this accomplished??
thanks
tony
If the values are plain strings you can use the following to create a sorted array:
NSArray *sorted = [values sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
This should do:
[self.data removeAllObjects];
NSArray *values = [[acacheDB.myDataset getRowsForTable:#"sites"] allValues];
NSSortDescriptor *alphaDescriptor = [[NSSortDescriptor alloc] initWithKey:#"DCFProgramName" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *sortedValues = [values sortedArrayUsingDescriptors:[NSMutableArray arrayWithObjects:alphaDescriptor, nil]];
[alphaDesc release];
[self.data addObjectsFromArray:sortedValues];
There's no need to clear an NSMutableArray if you're replacing it shortly afterwards.
There's no need to create an additional NSMutableDictionary, if you're not modifying anything in it.
There's no need to create an additional NSMutableArray, if you could just as well just add the values to the existing one.
Also: There are some serious memory leaks in your code. (2x alloc + 0x release = 2x leak)
Edit: updated code snippet to reflect OP's update on data structure.

How to sort NSMutableArray of NSMutableDictionary?

I have NSMutableArray of NSMutableDictionary(NSString objects). One of NSString object is actually a date, and i need to sort NSMutableArray based on that date and I don't want it to sort dates as strings. How can i make it?
If I understand correctly, your array contains dictionaries that contain strings and you want to sort on those strings... as dates. Something like this perhaps:
[someArray sortWithOptions: 0 usingComparator: ^(id inObj1, id inObj2) {
NSDate *date1 = [NSDate dateWithString: [inObj1 objectForKey: #"dateString"]];
NSDate *date2 = [NSDate dateWithString: [inObj2 objectForKey: #"dateString"]];
return [date1 compare: date2];
}];
You will need to use the sortedArrayUsingFunction:context: method. For example:
NSInteger comparator( NSDictionary *d1, NSDictionary *d2, void *context )
{
return [[d1 objectForKey:#"date"] compare:[d2 objectForKey:#"date"]];
}
// In some method:
NSArray *sortedArray = [array sortedArrayUsingFunction:comparator context:nil];
Note: This is not tested.

Help sorting an NSArray across two properties (with NSSortDescriptor?)

I'm a bit of a NSSortDescriptor n00b. I think, though, it is the right tool for what I need to do:
I have an NSArray consisting of objects with keys, say, "name" and "time". Instead of verbalizing it, here's an example:
input:
name: time
B: 4
C: 8
B: 5
C: 4
A: 3
C: 2
A: 1
A: 7
B: 6
desired output:
name: time
A: 1 <---
A: 3
A: 7
C: 2 <---
C: 4
C: 8
B: 4 <---
B: 5
B: 6
So the values are sorted by "time" and grouped by "name". A comes first because he had the smallest time value, and all values for A come after one another. Then comes C, he had the second smallest time value out of all his values. I have indicated the values that determine how the names are sorted; within each name group, sorting is by time.
How do I get from input to output NSArray in the most efficient way? (cpu- and memory-wise, not necessarily code-wise.) How would I construct the NSSortDescriptors for this, or use any other method? I don't want to roll my own unless it's the most efficient way.
My solution is:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
You can try it
The sortedArrayUsingDescriptors: NSArray method does most of what you need:
The first descriptor specifies the primary key path to be used in sorting the receiver’s contents. Any subsequent descriptors are used to further refine sorting of objects with duplicate values. See NSSortDescriptor for additional information.
Some filtering with NSPredicate is required too:
NSSortDescriptor *timeSD = [NSSortDescriptor sortDescriptorWithKey: #"time" ascending: YES];
NSMutableArray *sortedByTime = [UnsortedArray sortedArrayUsingDescriptors: timeSD];
NSMutableArray *sortedArray = [NSMutableArray arrayWithCapacity:[sortedByTime count]];
while([sortedByTime count])
{
id groupLead = [sortedByTime objectAtIndex:0];
NSPredicate *groupPredicate = [NSPredicate predicateWithFormat:#"name = %#", [groupLead name]];
NSArray *group = [sortedByTime filteredArrayUsingPredicate: groupPredicate];
[sortedArray addObjectsFromArray:group];
[sortedByTime removeObjectsInArray:group];
}
I have no idea if this is the most efficient method, but until you have reason to believe that it is causing problems there's no need to worry the performance implications. It's premature optimisation. I wouldn't have any concerns about the performance of this method. You've got to trust the framework otherwise you'll end up rewriting it (thus undermine the point of the framework) due to an unfounded paranoia.
I would create a new class called ItemGroup, and then add an extra ivar called group to your item class:
#interface ItemGroup : NSObject
{
NSNumber * time;
}
#property (nonatomic, copy) time;
#end
#interface ItemClass : NSobject
{
NSString * name;
NSNumber * time;
ItemGroup * group;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSNumber * time;
#property (nonatomic, assign) ItemClass * group; // note: must be assign
#end
Then, you could do the following:
NSMutableDictionary * groups = [NSMutableDictionary dictionaryWithCapacity:0];
for (ItemClass * item in sourceData)
{
ItemGroup * group = [groups objectForKey:item.name];
if (group == nil)
{
group = [[ItemGroup alloc] init];
[groups setObject:group forKey:item.name];
[group release];
group.time = item.time;
}
else if (item.time < group.time)
{
group.time = item.time;
}
item.group = group;
}
This code loops through the unsorted array, keeping track of the minimum time for each group, and also setting the group for each item. With that complete, you simply sort on group.time and time:
NSSortDescriptor * groupSorter;
groupSort = [NSSortDescriptor sortDescriptorWithKey:#"group.time" ascending:YES];
NSSortDescriptor * timeSorter;
timeSort = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObjects:groupSort, timeSort, nil];
NSArray * sorted = [sourceData sortedArrayUsingDescriptors:sortDescriptors];
And that should do the trick!
UPDATE: Note that you could get much better performance if you were able to assign the groups straight out of the gate. Something like this:
#interface ItemGroup : NSObject
{
NSString * name;
NSNumber * time;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSSNumber * time;
#end
#interface ItemClass : NSObject
{
ItemGroup * group;
NSNumber * time;
}
#property (nonatomic, retain) ItemGroup * group;
#property (nonatomic, copy) NSNumber * time;
#end
Now, if you maintain a list of groups somewhere (they could even go in an array somewhere, if need be):
ItemGroup * group_A = [[ItemGroup alloc] init];
group_A.name = #"A";
ItemGroup * group_B = [[ItemGroup alloc] init];
group_B.name = #"B";
...
And instead of setting the names of your data items, you set their group:
someItem.group = group_A;
someItem.time = GetSomeRandomTimeValue();
[sourceData addObject:someItem];
....
This would greatly simplify the loop used to set group times:
for (ItemClass * item in sourceData)
{
if (item.time < group.time) { group.time = item.time; }
}
And, if you really wanted to be blazing fast about it, you could even modify the property setter for your time property to set the group times on the fly:
#implementation ItemClass
- (void)setTime:(NSNumber *)newTime
{
if (newTime < group.time) { group.time = newTime; }
time = [newTime copy];
}
#end
Note that you would have to be sure that group had been set before you set the time. With this in place, you wouldn't need that sorting loop at all. The sortDescriptors would be enough.
I went through to make a little code (didn't try running it or really go over it so there might be a couple of mistakes, but it has the general idea) to do what you're looking for. Performance wise, it probably won't be the best if you start running into huge amounts of data. I'm sure there's a better way to do this, but I felt like doing it the most basic way as a "temporary fix" answer.
NSMutableArray *copiedarray = [YourFirstArray mutableCopy];
NSMutableArray *sortedarray = [[NSMutableArray alloc] init];
NSMutableArray *tempgroup = nil;
NSSortDescriptor * groupSorter = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSInteger i;
NSInteger savedlowest = -1;
NSString *savedname = #"";
while ([copiedarray count] > 0) {
///reset lowest time and group
savedlowest = -1;
savedname = #"";
///grab the lowest time and group name
for (ii = 0;ii < [copiedarray count]; ii++) {
if (savedlowest==-1 || ((YourClass *)([copiedarray objectAtIndex:ii])).time<savedlowest)) {
savedname = ((YourClass *)([copiedarray objectAtIndex:ii])).name;
savedlowest = ((YourClass *)([copiedarray objectAtIndex:ii])).time;
}
}
//we have the lowest time and the type so we grab all those items from the group
tempgroup = [[NSMutableArray alloc] init];
for (ii = [copiedarray count]-1;ii > -1; ii--) {
if ([((YourClass *)([copiedarray objectAtIndex:ii])).name isEqualToString:savedname]) {
///the item matches the saved group so we'll add it to our temporary array
[tempgroup addObject:[copiedarray objectAtIndex:ii]];
///remove it from the main copied array for "better performance"
[copiedarray removeObjectAtIndex:ii];
}
}
[tempgroup sortUsingDescriptors:[NSArray arrayWithObject:groupSorter]];
[sortedarray addObjectsFromArray:tempgroup];
[tempgroup release];
tempgroup = nil;
}
In the end you'll end up with what you're looking for in sortedarray.
You can use NSSortDescriptor. These descriptors are very useful as they let you do multiple key sort as well single key sorting. The case sensitivity and insensitivity is also easily achievable. I found a detailed example HERE
If you have to do more complicated sorting the just "ascending" can take care of (say sort NSString as if they were floats), you might want to do something like this:
NSDictionary *d = [self dictionaryFromURL:[NSURL URLWithString:urlStringValue]];
NSSortDescriptor *distanceSort = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES comparator:^(id left, id right) {
float v1 = [left floatValue];
float v2 = [right floatValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:#"company_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:distanceSort, nameSort, nil];
[distanceSort release];
NSArray *sortedObjects = [[d allValues] sortedArrayUsingDescriptors:sortDescriptors];
ILog();
return sortedObjects;

Resources