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.
Related
I have an NSMutableArray of dictionaries. I am using NSPredicate to filter through the array to find if a dictionary with a particular key exists or not.
I have referred to various examples, one of the closest is here: Using NSPredicate to filter an NSArray based on NSDictionary keys. However, I don't wish to have a value to the key. My problem is that I want to find the key first. I tried different syntaxes, but it did not help.
What I have done so far:
NSString *key = #"open_house_updated_endhour";
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K", key];
//NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K contains [cd]", key]; Doesn't work.
//NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K == %#", key]; Won't work because it expects a value here.
NSLog(#"predicate %#",predicateString);
NSArray *filtered = [updatedDateAndTime filteredArrayUsingPredicate:predicate]; // updatedDateAndTime is the NSMutableArray
A dictionary delivers nil as value for absent key. So simply compare the key against nil.
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K==NULL", key]; // Dictionaries not having the key
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K!=NULL", key]; // Dictionaries having the key
Try it:
NSArray *Myarray = [NSArray arrayWithObject:[NSMutableDictionary dictionaryWithObject:#"my hello string" forKey:#"name"]];
NSArray *filteredarray = [Myarray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(name == %#)", #"my hello string"]];
NSLog("%#",filteredarray);
Another example :
NSString *mycategory = #"iamsomeone";
NSArray *myitems = #[#{ #"types" : #[#"novel", #"iamsomeone", #"dog"] },
#{ #"types" : #[#"cow", #"iamsomeone-iam", #"dog"] },
#{ #"types" : #[#"cow", #"bow", #"cat"] }];
NSPredicate *mypredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSArray *categories = [evaluatedObject objectForKey:#"types"];
return [categories containsObject:mycategory];
}];
NSArray *outpuArray = [myitems filteredArrayUsingPredicate:mypredicate];
NSLog(#"hello output:%#",outpuArray);
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];
}
Does the IN operator work for filtering SBElementArrays? I have been trying to use it but it always returns a NULL array.
My code (hexArray will typically have more elements):
SBElementArray *musicTracks = [libraryPlaylist fileTracks];
hexArray = [NSArray arrayWithObject: #"3802BF81BD1DAB10"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY %K IN %#",#"persistentID",hexArray];
NSLog(#"%#", [[musicTracks filteredArrayUsingPredicate:predicate] valueForKey:#"persistentID"]);
NSLog(#"%#", hexArray);
NSLog(#"%#", predicate);
Output:
2013-05-26 12:59:29.907 test[1226:403] (null)
2013-05-26 12:59:29.907 test[1226:403] (3802BF81BD1DAB10)
2013-05-26 12:59:29.908 test[1226:403] ANY persistentID IN {"3802BF81BD1DAB10"}
I have tried setting the predicate to:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY %K == %#",#"persistentID",hexArray];
Output:
2013-05-26 13:03:04.629 test[1258:403] (3802BF81BD1DAB10)
2013-05-26 13:03:04.630 test[1258:403] (3802BF81BD1DAB10)
2013-05-26 13:03:04.630 test[1258:403] ANY persistentID == {"3802BF81BD1DAB10"}
And this works fine. But I would like the IN functionality.
Instead of doing
persistentID IN ('abc', 'abc', 'abc', ...)
you can do
persistentID == 'abc' OR persistentID == 'abc' OR ...
It seems to work pretty fast.
NSMutableArray *subPredicates = [NSMutableArray arrayWithCapacity:persistentIDs.count];
for (NSNumber *persistentID in persistentIDs) {
[subPredicates addObject:pred(#"persistentID == %#", persistentID.hexValue)];
}
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
[tracks filterUsingPredicate:predicate];
NSLog(#"%ld", tracks.count);
Try using CONTAINS[c]
Ex:-
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY %# CONTAINS[c] %k",hexArray, #"persistentID"];
I ended up just looping through all the elements of hexArray and using an equality predicate on each pass. Probably not the most efficient, but it works.
for (NSString *hexID in hexArray){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"persistentID == %#",hexID];
iTunesTrack *track = [[musicTracks filteredArrayUsingPredicate:predicate] objectAtIndex:0];
[track duplicateTo:playlist];
}
Your predicate should be %K IN %# (without the ANY), if I understand your intention correctly (get all the tracks that have one of the IDs in the array).
For some reason, this doesn't work with SBElementArray, but you could simply convert it to a regular NSArray before applying the predicate (an NSSet should work too, and might be more efficient):
SBElementArray *musicTracks = [libraryPlaylist fileTracks];
NSArray *musicTracksArray = [NSArray arrayWithArray:musicTracks];
NSArray *hexArray = [NSArray arrayWithObjects: #"CE24B292556DB1BA", #"CE24B292556DB1F0", #"CE24B292556DB1C4", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"persistentID", hexArray];
NSLog(#"%#", [[musicTracksArray filteredArrayUsingPredicate:predicate] valueForKey:#"persistentID"]);
Scripting Bridge technically supports the IN operator, in that it will construct a properly-formed Apple event for it, but most applications don't understand it. The best workaround is the chained OR tests as suggested by NSAddict.
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];
}
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;