Trouble sorting custom objects in an NSArray numerically - ios

If I'm trying to sort BL_Player by their playerScore property:
NSArray *sortedPlayers = [players sortedArrayUsingComparator: ^(BL_Player *a1, BL_Player *a2) {
return [a1.playerScore compare:a2.playerScore options:NSNumericSearch];
OR
NSArray *sortedPlayers = [players sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSInteger first = [(BL_Player *)a playerScore];
NSInteger second = [(BL_Player *)b playerScore];
return [first compare:second options:NSNumericSearch];
}];
both of which return bad receiver types. What's wrong with doing comparisons on ints or NSInteger?

Try this one:
NSArray *sortedPlayers = [players sortedArrayUsingComparator: ^(BL_Player *a1, BL_Player *a2) {
if (a1.playerScore > a2.playerScore) {
return NSOrderedAscending;
}
else if (a1.playerScore < a2.playerScore) {
return NSOrderedDescending;
}
else
return NSOrderedSame;
}
Change as per your requirement.
May help you.

A better approach to do this:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"playerScore"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [players sortedArrayUsingDescriptors:sortDescriptors];
for descending order change YES to NO.

Related

EXC_BREAKPOINT when using sortedArrayUsingComparator with a lot of objects

I'm trying to sort a set and it works fine most of the time. But when I start using my app on an old iPhone 5c and a lot of objects get into the set, I would eventually hit this error:
EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xdefe)
here is the code plus stacktrace:
if(manager.allBeacons){
NSSet *beacons = [manager.allBeacons copy];
NSSet *filteredSet = [[NSSet alloc] initWithSet:[beacons filteredSetUsingPredicate:predicate]];
if(filteredSet){
//NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"RSSI" ascending:NO];
//NSArray *sortDescriptors = [NSArray arrayWithObject:descriptor];
//NSArray *sortedArray = [[NSArray alloc] initWithArray:[filteredSet.allObjects sortedArrayUsingDescriptors:sortDescriptors]];
NSArray *sortedArray = [filteredSet.allObjects sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
if([obj1 isKindOfClass:[BLUSBeacon class]] && [obj2 isKindOfClass:[BLUSBeacon class]]){
BLUSBeacon *beacon1 = obj1;
BLUSBeacon *beacon2 = obj2;
if(beacon1.RSSI.integerValue > beacon2.RSSI.integerValue){
return (NSComparisonResult)NSOrderedAscending;
}
if(beacon1.RSSI.integerValue < beacon2.RSSI.integerValue){
return (NSComparisonResult)NSOrderedDescending;
}
}
return (NSComparisonResult)NSOrderedSame;
}];
if(sortedArray){
self.beacons = sortedArray;
[self.tableView reloadData];
}
}
}
And here is the last step in the stacktrace in case it serves any purpose:
As you can see, I was trying to sort with descriptors initially, but that made it crash even sooner.
Any thoughts?

Sort array in ascending order and remove duplicate values in objective- c

I have a horizontally and vertically scrollable table. I get the data for the header and first column from the web service(json). I want to sort the data in ascending order and remove duplicate data from both header and the first column. For removing duplicate values I used the following code:
-(void) requestFinished: (ASIHTTPRequest *) request
{
NSString *theJSON = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSMutableArray *jsonDictionary = [parser objectWithString:theJSON error:nil];
headData = [[NSMutableArray alloc] init];
NSMutableArray *head = [[NSMutableArray alloc] init];
leftTableData = [[NSMutableArray alloc] init];
NSMutableArray *left = [[NSMutableArray alloc] init];
rightTableData = [[NSMutableArray alloc]init];
for (NSMutableArray *dictionary in jsonDictionary)
{
Model *model = [[Model alloc]init];
model.cid = [[dictionary valueForKey:#"cid"]intValue];
model.iid = [[dictionary valueForKey:#"iid"]intValue];
model.yr = [[dictionary valueForKey:#"yr"]intValue];
model.val = [dictionary valueForKey:#"val"];
[mainTableData addObject:model];
[head addObject:[NSString stringWithFormat:#"%ld", model.yr]];
[left addObject:[NSString stringWithFormat:#"%ld", model.iid]];
}
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:head];
headData = [[orderedSet array] mutableCopy];
// NSSet *set = [NSSet setWithArray:left];
// NSArray *array2 = [set allObjects];
// NSLog(#"%#", array2);
NSOrderedSet *orderedSet1 = [NSOrderedSet orderedSetWithArray:left];
NSMutableArray *arrLeft = [[orderedSet1 array] mutableCopy];
//remove duplicate enteries from header array
[leftTableData addObject:arrLeft];
NSMutableArray *right = [[NSMutableArray alloc]init];
for (int i = 0; i < arrLeft.count; i++)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int j = 0; j < headData.count; j++)
{
/* NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.iid == %ld", [[arrLeft objectAtIndex:i] intValue]];
NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];*/
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.iid == %ld AND SELF.yr == %ld", [[arrLeft objectAtIndex:i] intValue], [[headData objectAtIndex:j] intValue]];
NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];
if([filteredArray count]>0)
{
Model *model = [filteredArray objectAtIndex:0];
[array addObject:model.val];
}
}
[right addObject:array];
}
[rightTableData addObject:right];
}
How will I sort the arrays in ascending order?
Please help.
OK, so you have a model object that looks something like this...
#interface Model: NSObject
#property NSNumber *idNumber;
#property NSNumber *year;
#property NSString *value;
#end
Note, I am intentionally using NSNumber and not NSInteger for reasons that will become clear.
At the moment you are trying to do a lot all in one place. Don't do this.
Create a new object to store this data. You can then add methods to get the data you need. Seeing as you are displaying in a table view sectioned by year and then each section ordered by idNumber then I'd do something like this...
#interface ObjectStore: NSObject
- (void)addModelObject:(Model *)model;
// standard table information
- (NSInteger)numberOfYears;
- (NSInteger)numberOfIdsForSection:(NSinteger)section;
// convenience methods
- (NSNumber *)yearForSection:(NSInteger)section;
- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row;
- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row;
// now you need a way to add objects
- (void)addModelObject:(Model *)model;
#end
Now to implement it.
We are going to store everything in one dictionary. The keys will be years and the objects will be dictionaries. In these dictionaries the keys will be idNumbers and the objects will be arrays. These array will hold the models.
So like this...
{
2010 : {
1 : [a, b, c],
3 : [c, d, e]
},
2013 : {
1 : [g, h, u],
2 : [e, j, s]
}
}
We'll do this with all the convenience methods also.
#interface ObjectStore: NSObject
#property NSMutableDictionary *objectDictionary;
#end
#implementation ObjectStore
+ (instancetype)init
{
self = [super init];
if (self) {
self.objectDictionary = [NSMutableDictionary dictionary];
}
return self;
}
+ (NSInteger)numberOfYears
{
return self.objectDictionary.count;
}
+ (NSInteger)numberOfIdsForSection:(NSinteger)section
{
// we need to get the year for this section in order of the years.
// lets create a method to do that for us.
NSNumber *year = [self yearForSection:section];
NSDictionary *idsForYear = self.objectDictionary[year];
return idsForYear.count;
}
- (NSNumber *)yearForSection:(NSInteger)section
{
// get all the years and sort them in order
NSArray *years = [[self.obejctDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
// return the correct year
return years[section];
}
- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row
{
// same as the year function but for id
NSNumber *year = [self yearForSection:section];
NSArray *idNumbers = [[self.objectDictionary allKeys]sortedArrayUsingSelector:#selector(compare:)];
return idNumbers[row];
}
- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row
{
NSNumber *year = [self yearForSection:section];
NSNumber *idNumber = [self idForSection:section row:row];
return self.objectDictionary[year][idNumber];
}
// now we need a way to add objects that will put them into the correct place.
- (void)addModelObject:(Model *)model
{
NSNumber *modelYear = model.year;
NSNumber *modelId = model.idNumber;
// get the correct storage location out of the object dictionary
NSMutableDictionary *idDictionary = [self.objectDictionary[modelYear] mutableCopy];
// there is a better way to do this but can't think atm
if (!idDictionary) {
idDictionary = [NSMutableDictionary dictionary];
}
NSMutableArray *modelArray = [idDictionary[modelId] mutableCopy];
if (!modelArray) {
modelArray = [NSMutableArray array];
}
// insert the model in the correct place.
[modelArray addObject:model];
idDictionary[modelId] = modelArray;
self.objectDictionary[modelYear] = idDictionary;
}
#end
With all this set up you can now replace your complex function with this...
-(void) requestFinished: (ASIHTTPRequest *) request
{
NSString *theJSON = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary *jsonDictionary = [parser objectWithString:theJSON error:nil];
for (NSDictionary *dictionary in jsonDictionary)
{
Model *model = [[Model alloc]init];
model.cid = [dictionary valueForKey:#"cid"];
model.idNumber = [dictionary valueForKey:#"iid"];
model.year = [dictionary valueForKey:#"yr"];
model.val = [dictionary valueForKey:#"val"];
[self.objectStore addModelObject:model];
}
}
To get the models out for a particular row then just use...
[self.objectStore modelsForSection:indexPath.section row:indexPath.row];
To get the number of sections in the tableview delegate method...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.objectStore numberOfYears];
}
No messing around with the model in the view controller.
Welcome to the MVC pattern.
There's a crap ton of code here but by placing all the code here you can remove all the complex code from your VC.
NSSet keeps only non-duplicate objects within themselves so to keep only unique objects in array you can use NSSet as -
Suppose you have array with duplicate objects
NSArray *arrayA = #[#"a", #"b", #"a", #"c", #"a"];
NSLog(#"arrayA is: %#", arrayA);
//create a set with the objects from above array as
//the set will not contain the duplicate objects from above array
NSSet *set = [NSSet setWithArray: arrayA];
// create another array from the objects of the set
NSArray *arrayB = [set allObjects];
NSLog(#"arrayB is: %#", set);
The output from the above looks like:
arrayA is: (
a,
b,
a,
c,
a
)
arrayB is: {(
b,
c,
a
)}
and to sort a mutable array in ascending order you can use NSSortDescriptor and sortUsingDescriptors:sortDescriptors. Also you need to provide the key on the basis of which array will be sorted.
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"key" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[array sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
Here you will get what you want.
//sort description will used to sort array.
NSSortDescriptor *descriptor=[[NSSortDescriptor alloc] initWithKey:#"iid" ascending:YES];
NSArray *descriptors=[NSArray arrayWithObject: descriptor];
NSArray *reverseOrder=[arrLeft sortedArrayUsingDescriptors:descriptors];
reverseOrder is your desire output.
there is another way you can sort objects that followed model.
NSArray *someArray = /* however you get an array */
NSArray *sortedArray = [someArray sortedArrayUsingComparator:^(id obj1, id obj2) {
NSNumber *rank1 = [obj1 valueForKeyPath:#"iid"];
NSNumber *rank2 = [obj2 valueForKeyPath:#"iid"];
return (NSComparisonResult)[rank1 compare:rank2];
}];
here sortedArray is our output.
you can replace same things for yr key as well.
This is what I did to sort the header data in ascending order and to remove duplicates from both header and leftmost column. Hope this will help others
NSOrderedSet *orderedSet3 = [NSOrderedSet orderedSetWithArray:head3];
headData3 = [[orderedSet3 array] mutableCopy];
[headData3 sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2)
{
return [str1 compare:str2 options:(NSNumericSearch)];
}];
NSOrderedSet *orderedSet4 = [NSOrderedSet orderedSetWithArray:left3];
NSMutableArray *arrLeft3 = [[orderedSet4 array] mutableCopy];
[leftTableData3 addObject:arrLeft3];

Obj-C - How do I protect this sorted array from null values

I have some Json input arrays that I want to sort. It is possible that some of the sort descriptors have null values. How can I protect against null value exceptions? Thanks.
What I have at present is:
//sort the group arrays
NSSortDescriptor *sortDescriptor1a;
NSSortDescriptor *sortDescriptor1b;
sortDescriptor1a = [[NSSortDescriptor alloc] initWithKey:#"Min" ascending:YES selector:#selector(caseInsensitiveCompare:)];
sortDescriptor1b = [[NSSortDescriptor alloc] initWithKey:#"Line" ascending:YES selector:#selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors1 = [NSArray arrayWithObjects:sortDescriptor1a, sortDescriptor1b, nil];
NSArray *tempArray1 = [unsortedGroup1 sortedArrayUsingDescriptors:sortDescriptors1 ];
currentGroup1 = [tempArray1 mutableCopy];
NSSortDescriptor *sortDescriptor2a;
.....
What is the sort behaviour that you desire in the case of nil values? The following will push nil values to the end of the array.
NSArray *sortedGroup1 = [unsortedGroup1 sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSComparisonResult result = NSOrderedSame;
if (obj1.Min && obj2.Min) {
result = [obj1.Min caseInsensitiveCompare:obj2.Min];
} else if (obj1.Min) {
result = NSOrderedDescending;
} else if (obj2.Min) {
result = NSOrderedAscending;
}
if (result == NSOrderedSame) {
if (obj1.Line && obj2.Line) {
result = [obj1.Line caseInsensitiveCompare:obj2.Line];
} else if (obj1.Line) {
result = NSOrderedDescending;
} else if (obj2.Line) {
result = NSOrderedAscending;
}
}
return result;
}];

Sorting NSArray by file creation time

I've read all answers about the subject, but still have my array unsorted. Please, help me with this issue. What's wrong with the code? Thanks in advance.
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filename error:NULL];
NSDate *date = [attributes fileCreationDate];
NSMutableArray *datesList = [[NSMutableArray alloc] init];
[datesList addObject:date];
NSArray *sortedArray = [[NSArray alloc] init];
sortedArray = [datesList sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
if ([obj1 date] > [obj2 date])
{
return (NSComparisonResult)NSOrderedAscending;
}
if ([obj1 date] < [obj2 date])
{
return (NSComparisonResult)NSOrderedDescending;
}
return (NSComparisonResult)NSOrderedSame;
}];
NSLog(#"sortedarray:%#",sortedArray);
First of all, you add to the array only one object :)
Your code should look like:
sortedArray = [datesList sortedArrayUsingComparator:^NSComparisonResult(NSDate *first, NSDate *second){
return [first compare:second];
}];
[NSDate compare:] returns `NSComparisonResult which is fine. You can add ! if you want to have opposite sorting direction.

Sorting Array issue in iOS app

I have an array like:
(
"8461.86","8468.22","8468.22","8162.59","8164.50","7280.59" )
I try to sort this array decendingly as per following:
sortedArray = [arr_distance sortedArrayUsingComparator:^(id firstObject, id secondObject) {
return [((NSString *)firstObject) compare:((NSString *)secondObject) options:NSNumericSearch];
}];
But i got following array like:
(
"7280.59","8162.59","8164.50","8461.86","8468.22","8468.22" )
Why this happen?
I also try with like
return [firstObject compare:secondObject options:NSNumericSearch];
But not getting proper sorted array.
NSSortDescriptor *desc = [[NSSortDescriptor alloc]initWithKey:#"" ascending:NO];
NSMutableArray *yourTempArray = [[NSMutableArray alloc]initWithObjects:#"8459.93",#"8466.28",#"8466.28",#"8160.70",#"8162.61",#"7278.56", nil];
[yourTempArray sortUsingDescriptors:[NSArray arrayWithObject:desc]];
NSLog(#"sotedAr == %#",yourTempArray);
Output:
sotedAr == (
"8466.28",
"8466.28",
"8459.93",
"8162.61",
"8160.70",
"7278.56"
)
To sort in descending order, you could just switch the firstObject and secondObject in your code:
sortedArray = [arr_distance sortedArrayUsingComparator:^(id firstObject, id secondObject) {
return [((NSString *)secondObject) compare:((NSString *)firstObject) options:NSNumericSearch];
}];
Try following code:-
NSSortDescriptor *frequencyDescriptor = [NSSortDescriptor sortDescriptorWithKey:#""
ascending:NO
comparator:^(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
NSEnumerator * enumerator = [arr_distance objectEnumerator];
NSArray * descriptors = [NSArray arrayWithObjects:frequencyDescriptor, nil];
NSArray * sortedArray = [arr_distance sortedArrayUsingDescriptors:descriptors];
enumerator = [sortedArray objectEnumerator];
NSLog(#"sortedArray = %#",sortedArray);
// use sortedArray which is sorted
Below code may be useful for sorting your array in descending order
NSArray *arr = [NSArray arrayWithObjects:#"8459.93",#"8466.28",#"8466.28",#"8160.70",#"8162.61",#"7278.56", nil];
NSMutableArray *yourTempArray = [[NSMutableArray alloc]init];
[yourTempArray addObjectsFromArray:arr];
NSLog(#"before sorting = %#",yourTempArray);
NSSortDescriptor* sort = [NSSortDescriptor sortDescriptorWithKey: #"self" ascending: NO];
[yourTempArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];
NSLog(#"After sorting = %#",yourTempArray);

Resources