about searching by using NSPredicate - objective-c

This is the structure of my class ContentItem
#interface ContentItem : NSObject {
//properties of this class
NSString *name;
NSString *type;
...
}
an NSArray has above class's objects.
I want to search the objects by searching words.
So I tried to do this.
NSArray *filteredItem = [array filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat: #"SELF contains[cd] %#", searchString]];
result:
Can't use in/contains operator with collection
<ContentItem: 0x68309b0> (not a collection)
Actually I don't know how to search the objects.
Can anyone help?
Thanks.

I think you need to access the property you are intending to search on. See the Apple docs for Using Predicates with Key-Paths
e.g.
NSArray *filteredItem = [array filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat: #"SELF.name contains[cd] %#", searchString]];
for both properties
NSArray *filteredItem = [array filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat: #"SELF.name contains[cd] %# OR SELF.type contains[cd] %#", searchString, searchString]];

Related

Use NSPredicate to filter by index

Basically, what I want to do is this:
NSArray *objectsAtIndex1 = #[[#[#"Foo", #"Bar"] objectAtIndex:1]];
but using NSPredicate instead, so it would look something like this (however this is not working):
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF[1] != null"]
NSArray *objectsAtIndex1 = [#[#"Foo", #"Bar"] filteredArrayUsingPredicate:predicate]
And of course, #"Foo" and #"Bar" are in reality unknown values (and could even be dictionaries or numbers). Is it possible to achieve this?
I solved it by using the predicateWithBlock: method on NSPredicate like this:
NSArray *fooBarArray = #[#"Foo", #"Bar"];
NSUInteger index = 1;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [fooBarArray indexOfObject:evaluatedObject] == index;
}];
NSArray *objectsAtIndex1 = [fooBarArray filteredArrayUsingPredicate:predicate];
One caveat is however that the objects in fooBar-array needs to be unique for this to work, which is not perfect.

NSPredicate, search in a NSArray, inside a NSArray of NSDict

I have a NSArray of NSDictionary.
One of the keys of the NSDictionary contains a NSArray of strings.
Is there a way that I can use NSPredicate to find a specific strins in that Array of strings?
Thanks :)
Also: This work great, but not for sublevelArray
predicate = [NSPredicate predicateWithFormat:#" %K LIKE[cd] %#", sKey, sLookForString];
Just replace LIKE with CONTAINS in your format string. For example, given this array:
NSArray *dogs = #[#{#"name" : #"Fido",
#"toys" : #[#"Ball", #"Kong"]},
#{#"name" : #"Rover",
#"toys" : #[#"Ball", #"Rope"]},
#{#"name" : #"Spot",
#"toys" : #[#"Rope", #"Kong"]}];
...the following predicate can be used obtain a filtered array containing only the dictionaries where the value for the key toy is an array that contains the string Kong.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K CONTAINS[cd] %#", #"toys", #"Kong"];
On NSArray you can use filteredArrayUsingPredicate:, on NSDictionary use enumerateKeysAndObjectsUsingBlock: and then for each value do either a filteredArrayUsingPredicate: if it is an NSArray or you can use evaluateWithObject: using the predicate itself.
If you want to filter the array of dictionaries based on the array of strings, you can use -predicateWithBlock to filter the array, as shown in the code below:
- (NSArray *)filterArray:(NSArray *)array WithSearchString:(NSString *)searchString {
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSDictionary *dictionary = (NSDictionary *)evaluatedObject;
NSArray *strings = [dictionary objectForKey:#"strings"];
return [strings containsObject:searchString];
}];
return [array filteredArrayUsingPredicate:predicate];
}

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.”

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.