Sorting results in Obj-c - objective-c

I need to be able to sort the results of my sort method, but it's unclear to me how to do that, do I need to just run a simular method again on the previous results or can it be done in one method?
Here's my method
-(NSArray*)getGameTemplateObjectOfType:(NSString *) type
{
NSArray *sortedArray;
if(editorMode == YES)
{
sortedArray = kingdomTemplateObjects;
}
else
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type CONTAINS[cd] %#", type];
NSArray *newArray = [kingdomTemplateObjects filteredArrayUsingPredicate:predicate];
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:type
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
sortedArray = [newArray sortedArrayUsingDescriptors:sortDescriptors];
}
return sortedArray;
}
type is being set to "Building" which returns all the building types in my game, but what if I then want those results sorted alphabetically according to their name? or perhaps sorted by which building is the most expensive from it's gold value?

You have to parse the array twice. NSPredicate does not provide a means to sort. Check out the NSPredicate Programming Guide. What I did actually was to quickly scan the NSPredicate BNF Syntax to look for obvious signs of sorting operators, such as ASC or DESC. Nothing is there.
Also, there are a number of similar questions here on SO:
How to sort NSPredicate
NSPredicate Sort Array and Order DESC
NSSortDescriptor and NSPredicate for sorting and filtering
To tell your getGameTemplateObjectOfType: how you want the results sorted, you might pass in some key for sorting. For example:
-(NSArray *)getGameTemplateObjectOfType:(NSString *)type sortedByKey:(NSString *)key ascending:(BOOL)ascending;
But to do so would likely complicate your code - you will have to handle all combinations of key and type inside your function. (Let me know if you don't understand what I'm saying here).
In the end it may be that you resign your filtering function getGameTemplateObjectOfType: to just that: filtering. And if the client of that function wants the results sorted in some fashion, then the client can do so. And then you will discover why it is that Apple has kept the functionalities separated.

in your code, if [kingdomTemplateObjects filteredArrayUsingPredicate:predicate]; returns the correct results
then you can use [newArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)]; to sort your array.
-(NSArray*)getGameTemplateObjectOfType:(NSString *) type
{
NSArray *sortedArray;
if(editorMode == YES)
{
sortedArray = kingdomTemplateObjects;
}
else
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type CONTAINS[cd] %#", type];
NSArray *newArray = [kingdomTemplateObjects filteredArrayUsingPredicate:predicate];
sortedArray = [newArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
return sortedArray;
}

Related

How do you filter an NSArray with an Array of NSPredicates?

I know you can do something like [..."SELF.some_id == [c] %d AND SELF.some_id == [c] %d", id1, id2], but I need more than this. Is there a way to do this without building a string.
For example...
NSArray *arrayOfWantedWidgetIds = #[1,3,5,6,9,13,14,16];
NSMutableArray *allWidgets = [[WidgetManager sharedWidget] getAllWidgets];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.widget_id ==[c] %d", arrayOfWantedWidgetIds]; //obviously we can't do this, it won't accept an array of IDS for %d
[allWidgets filterArrayUsingPredicate:predicate];
How can I achieve something like this? Another way... if I looped this, and made individual predicates for each value in arrayOfWantedWidgetIds, and then add all the individual predicates into an array... this doesn't help either as filterArrayUsingPredicate only accepts an NSPredicate. Not an array of them.
You cannot pass an array to the predicate API like that. However, you can pass an array of IDs, and use the IN operator, which should be sufficient in your specific case:
NSArray *arrayOfWantedWidgetIds = #[#1, #3, #5, #6, #9, #13, #14, #16];
NSMutableArray *allWidgets = [[WidgetManager sharedWidget] getAllWidgets];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.widget_id IN %#", arrayOfWantedWidgetIds];
[allWidgets filterArrayUsingPredicate:predicate];
You can also construct a composite predicate with NSCompoundPredicate, but this is unnecessary in this situation.
Look at the in operator
NSArray *idList = #[#1,#2,#3,#5,#7];
NSMutableArray *widgetList = [[NSMutableArray alloc] init];
for (int i=0; i<20; i++) {
[widgetList addObject:[[widgets alloc] init]];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.widget_id in %#",idList];
NSLog(#"%#",[widgetList filteredArrayUsingPredicate:predicate].debugDescription);

NSCompoundPredicate

I'm trying to filter a UITableView's data using a UISearchDisplayController and NSCompoundPredicate. I have a custom cell with 3 UILabels that I want to all be filtered within the search, hence the NSCompoundPredicate.
// Filter the array using NSPredicate(s)
NSPredicate *predicateName = [NSPredicate predicateWithFormat:#"SELF.productName contains[c] %#", searchText];
NSPredicate *predicateManufacturer = [NSPredicate predicateWithFormat:#"SELF.productManufacturer contains[c] %#", searchText];
NSPredicate *predicateNumber = [NSPredicate predicateWithFormat:#"SELF.numberOfDocuments contains[c] %#",searchText];
// Add the predicates to the NSArray
NSArray *subPredicates = [[NSArray alloc] initWithObjects:predicateName, predicateManufacturer, predicateNumber, nil];
NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
However, when I do this, the compiler warns me:
Incompatible pointer types initializing 'NSCompoundPredicate *_strong'
with an expression of type 'NSPredicate *'
Every example I've seen online does this exact same thing, so I'm confused. The NSCompoundPredicate orPredicateWithSubpredicates: method takes an (NSArray *) in the last parameter, so I'm REALLY confused.
What's wrong?
First of all, using "contains" is very slow, consider mayber "beginswith"?
Second, what you want is:
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Three, you could've just done something like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.productName beginswith[cd] %# OR SELF.productManufacturer contains[cd] %#", searchText, searchText];
orPredicateWithSubpredicates: is defined to return an NSPredicate*. You should be able to change your last line of code to:
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
... and still have all of the compoundPredicates applied.
Here's an useful method i created based on the answers above (which i thank very much!)
It allows to create an NSPredicate dynamically, by sending an array of filter items and a string which represents the search criteria.
In the original case, the search criteria changes, so it should be an array instead of a string. But it may be helpful anyway
- (NSPredicate *)dynamicPredicate:(NSArray *)array withSearchCriteria:(NSString *)searchCriteria
{
NSArray *subPredicates = [[NSArray alloc] init];
NSMutableArray *subPredicatesAux = [[NSMutableArray alloc] init];
NSPredicate *predicate;
for( int i=0; i<array.count; i++ )
{
predicate = [NSPredicate predicateWithFormat:searchCriteria, array[i]];
[subPredicatesAux addObject:predicate];
}
subPredicates = [subPredicatesAux copy];
return [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
}

How to get strings with specific length from NSArray?

I want to get strings that have specific length in NSArray.
The array has many elements and I don't want to use fast enumeration.
Is there a possible way?
This works like a charm:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.length == %d", lenght];
NSArray *filtered = [array filteredArrayUsingPredicate:predicate];
No matter what you do you will be using fast enumeration whether you realize it or not. However, have you considered using an NSPredicate object and the filteredArrayWithPredicate method?
NSArray *yourArray = [[NSArray alloc] initWithObjects:#"Apple, Orange, Grapes, Cherry, nil"];
for(NSString *element in yourArray){
if(element.length==yourLength){
[filteredArray addObject:element];
}
}
NSLog(#"Filtered array now contains the elements with length %d", yourLength);
NSLog(#"Filtered array--%#", filteredArray);

How to sort NSPredicate

I am trying to sort my array while using NSPredicate.. I have read other places that possibly using NSSortDescriptor could be an option. Having some trouble figuring this out.
I am attempting to sort my array by companyName.
Any advice appreciated, thanks
Greg
- (void)filterSummaries:(NSMutableArray *)all byNameThenBooth:(NSString*) text results:(NSMutableArray *)results
{
[results removeAllObjects];
if ((nil != text) && (0 < [text length])) {
if ((all != nil) && (0 < [all count])) {
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"companyName contains[cd] %# OR boothNumber beginswith %#", text, text];
[results addObjectsFromArray:[all filteredArrayUsingPredicate:predicate]];
}
}
else {
[results addObjectsFromArray:all];
}
}
you have several options how to sort an array:
I'll show a NSSortDescriptor-based approach here.
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"companyName contains[cd] %# OR boothNumber beginswith %#",
text,
text];
// commented out old starting point :)
//[results addObjectsFromArray:[all filteredArrayUsingPredicate:predicate]];
// create a descriptor
// this assumes that the results are Key-Value-accessible
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"companyName"
ascending:YES];
//
NSArray *results = [[all filteredArrayUsingPredicate:predicate]
sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
// the results var points to a NSArray object which contents are sorted ascending by companyName key
This should do your job.
The filteredArrayUsingPredicate: function walks through your array and copies all objects that match the predicate into a new array and returns it. It does not provide any sorting whatsoever. It's more of a search.
Use the sorting functions of NSArray, namely sortedArrayUsingComparator:, sortedArrayUsingDescriptors:, sortedArrayUsingFunction:context: and the like, whichever serves you most.
Checkout NSArray Class Reference for details.
BTW: If you want to sort lexically, you may use sortedArrayUsingSelector:#selector(compare:) which will use NSString's compare: function to find the right order.

How to quickly search an array of objects in Objective-C

Is there a way in Objective-C to search an array of objects by the contained object's properties if the properties are of type string?
For instance, I have an NSArray of Person objects. Person has two properties, NSString *firstName and NSString *lastName.
What's the best way to search through the array to find everyone who matches 'Ken' anywhere in the firstName OR lastName properties?
try something like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"firstName==%# OR lastName==%#",#"Ken",#"Ken"];
NSArray *results = [allPersons filteredArrayUsingPredicate:predicate];
Short answer: NSArray:filteredArrayUsingPredicate:
Long answer: Predicate Programming Guide
You can simply use NSPredicate to filter your search from actual result array:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.property_name contains[c] %#",stringToSearch];
filteredPendingList = [NSMutableArray arrayWithArray:[mainArr filteredArrayUsingPredicate:predicate]];
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"property_name"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [filteredPendingList sortedArrayUsingDescriptors:sortDescriptors];
So you will be getting the sorted array with filtered result. property_name above is the name of variable inside your object on which you want to perform your search operation. Hope it will help you.
You'll have to do a linear search, comparing each entry in the array to see if it matches what you're looking for.