unable to set NSPredicateEditor with NOT operator and single subpredicate - objective-c

I have an NSPredicateEditor instance named editor configured with the following row templates:
NSMutableArray *finalTemplates = [NSMutableArray arrayWithCapacity:2];
for(NSString *keyPath in #[#"sampleName",#"comment"]) {
NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions:#[[NSExpression expressionForKeyPath:keyPath]]
rightExpressionAttributeType:NSStringAttributeType
modifier:NSDirectPredicateModifier
operators:#[#(NSContainsPredicateOperatorType), #(NSBeginsWithPredicateOperatorType)]
options: 0];
[finalTemplates addObject:template];
}
NSArray *compoundTypes = #[#(NSNotPredicateType), #(NSAndPredicateType), #(NSOrPredicateType)];
NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:compoundTypes];
editor.rowTemplates = [#[compound] arrayByAddingObjectsFromArray:finalTemplates];
I want to make it show this:
This seems simple enough, but I can't make it work. When I try:
editor.objectValue = [NSPredicate predicateWithFormat: #"NOT sampleName CONTAINS ''"];
editor.objectValue = [NSPredicate predicateWithFormat: #"NONE sampleName CONTAINS ''"];
editor.objectValue = [NSCompoundPredicate notPredicateWithSubpredicate: [NSPredicate predicateWithFormat: #"sampleName CONTAINS ''"]];
each results in an exception:
unable to find template matching predicate (...)
When I do:
editor.objectValue = [NSPredicate predicateWithFormat: #"NOT (sampleName CONTAINS '' OR comment CONTAINS '')"];
this works, but it's not what I want.
IOW, how can I make it show the "None" row with only one other row?
Note: I have no issue making it show the "Any" or "All" row template following by "sampleName contains ...".

NONE is NOT ( OR ). I don't think there's a format but in code it is:
[NSCompoundPredicate notPredicateWithSubpredicate:
[NSCompoundPredicate orPredicateWithSubpredicates:
#[[NSPredicate predicateWithFormat: #"sampleName CONTAINS ''"]]]];

Related

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];
}

Performing a search on an NSArray from a UISearchBar

I'm trying to perform a search on some results from a JSON feed that have been loaded into an NSArray called tableData and then displayed in a UITable.
The searchResults NSArray has been declared the .h file. The problem is that at the moment searchResults are empty and output nothing to the console. I'm not sure why...
I'm wondering if there's something missing from the
searchResults = [tableData filteredArrayUsingPredicate:resultPredicate];
line of code below
thanks for any help.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = [tableData filteredArrayUsingPredicate:resultPredicate];
NSLog(#"searchResults: %#", searchResults);
NSLog(#"tableData results: %#", tableData);
}
If you are looking for a field called 'cat' in your Dictionary, instead of having the predicate look in SELF, it should look in SELF.cat.
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.cat CONTAINS[cd] %#", searchText];
If you still need to check other fields contained within the object, you can create a compound predicate. Details on Predicates can be found in Apple's Predicate Programming Guide.
Try to replace [cd] -> [c]:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"self.youProperty contains[c] %#", searchString];
like[cd] means “case- and diacritic-insensitive like.”

How to modify this predicate so it handles multiple keywords efficiently

I have this predicate:
NSPredicate * thePredicateKeyword = [NSPredicate predicateWithFormat:#"any keywords.thekeyword beginswith [cd] %#", searchTerm];
Basically each business have many to many relationship with keywords.
But suppose I do not have one searchTerm. Say I have an array.
How would I do so?
I suppose I can just make predicate for each and combine them with or predicate, etc.
However, is there a way to more efficiently do this using in keywords or stuff like that?
What about a function that returns something like this:
-(NSPredicate *)createCompoundPredicateForSearchTerms:(NSArray *)searchTerms
{
NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
NSEnumerator *searchTermEnum = [searchTerms objectEnumerator];
NSString *searchTerm;
while (searchTerm = [searchTermEnum nextObject]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"keywords.thekeyword beginswith [cd] %#", searchTerm];
[subPredicates addObject:predicate];
}
return [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
}
This is what I actually use. However, the anwer I chose is what inspire it.
NSArray * keywords = [searchTerm componentsSeparatedByString:#" "];
NSMutableArray * keywordPredicates = [NSMutableArray array];
for (NSString * aKeyword in keywords) {
NSPredicate * thePredicateKeyword = [NSPredicate predicateWithFormat:#"any keywords.thekeyword beginswith [cd] %#", aKeyword];
[keywordPredicates addObject:thePredicateKeyword];
}
NSPredicate * thePredicateKeyword = [NSCompoundPredicate orPredicateWithSubpredicates:keywordPredicates];
return thePredicateKeyword;

NSPredicate concatenating attributes

I m trying to figure out how to concatenate attribute names. I have a county and a district attribute that I want to query like
[NSPredicate predicateWithFormat:#"county + district contains[cd] %#",searchBar.text]
gives me unimplemented SQL generation for predicate error. and I am not sure how to implement NSPredicate.
Thanks
This should give you an idea of how to do some more complicated searching. It will match queries for "county district", "district county", etc.
NSArray *searchTerms = [searchBar.text componentsSeparatedByString:#" "];
NSString *predicateFormat = #"(county contains[cd] %#) OR (district contains[cd] %#)";
NSPredicate *predicate;
if ([searchTerms count] == 1) {
NSString *term = [searchTerms objectAtIndex:0];
predicate = [NSPredicate predicateWithFormat:predicateFormat, term, term];
} else {
NSMutableArray *subPredicates = [NSMutableArray array];
for (NSString *term in searchTerms) {
NSPredicate *p = [NSPredicate predicateWithFormat:predicateFormat, term, term];
[subPredicates addObject:p];
}
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
}
[fetchRequest setPredicate:predicate];
See Cocoa Is My Girlfriend: Adding iTunes-style search to your Core Data application for more explanation.
I ended up implementing another field as concatenating the two variables(district+country) and perform the query on that variable.
I did something similar and concluded that like cekisakurek, the best method was to concatenate the fields into a common field (more relevant for first/last name)
- (NSString *)fullName {
return [NSString stringWithFormat:#"%# %#", self.firstName, self.lastName];
}
and then filtered on this field using 'contains[cd]'
[NSPredicate predicateWithFormat:#"fullName contains[cd] %#", self.searchBar.text];
[NSPredicate predicateWithFormat:#"county contains[cd] %# AND district contains[cd] %#",searchBar.text,searchBar.text];
Just try the above lines of code, it would helps you.

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.