I give up. I have tried every combination I can imagine to check if a string contains another string. Here's an example of intuitive syntax describing what I want to do:
NSPredicate* pPredicate = [NSPredicate predicateWithFormat:#"NOT (%K CONTAINS[c] %#)",
NSMetadataItemFSNameKey,
[NSString stringWithFormat:#"Some String"]];
Regardless of how I shift the NOT around, use the ! operator instead, shift the parentheses or remove them altogether, I always get an exception parsing this expression.
What is wrong with this expression?
EDIT: The exception happens when I call
[pMetadataQuery setPredicate:pPredicate];
and the exception is: * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unknown type of NSComparisonPredicate given to NSMetadataQuery (kMDItemFSName CONTAINS[c] "Some String")'
I had complete success with:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"NOT (%K CONTAINS[c] %#)",
#"someKey",
[NSString stringWithFormat:#"Some String"]];
NSArray *testArray =
[NSArray arrayWithObjects:
[NSDictionary dictionaryWithObject:#"This sure is Some String" forKey:#"someKey"],
[NSDictionary dictionaryWithObject:#"I've nothing to say" forKey:#"someKey"],
[NSDictionary dictionaryWithObject:#"I don't even have that key" forKey:#"someOtherKey"],
nil];
NSArray *filteredArray = [testArray filteredArrayUsingPredicate:predicate];
NSLog(#"found %#", filteredArray);
The second two objects of the three in testArray ended up in filteredArray, under OS X v10.7 and iOS v4.3. So the issue isn't the predicate — making this technically a complete answer to the question — it's some sort of restriction in NSMetadataQuery. Sadly I've no experience in that area, but it's certainly the next thing to research.
Swift 3.0
let predicate = NSPredicate(format: "NOT (%K CONTAINS[c] %#)", "someKey", "Some String")
let testArray: [Any] = [[ "someKey" : "This sure is Some String" ], [ "someKey" : "I've nothing to say" ], [ "someOtherKey" : "I don't even have that key" ]]
let filteredArray: [Any] = testArray.filter { predicate.evaluate(with: $0) }
print("found \(filteredArray)")
Related
I have an NSArray containing JSON string like below.
[
{
"title":"find good book",
"isCompleted":true
},
{
"title":"complete work",
"isCompleted":false
},
{
"title":"check schedule",
"isCompleted":false
}
]
I want to find indexes of objects having "plet" value under "title" key. When I just simply used below NSPredicate, it returned every objects, because every JSON objects having "isCompleted" stings as a key.
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self CONTAINS[cd] %#", #"plet"];
I don't want to convert JSON to NSDictionary object. And I don't use [NSPredicate predicateWithBlock:], because I am going to apply this predicate to NSFetchedResultsController. According to document, NSPredicate which is created by [NSPredicate predicateWithBlock:] can't be used to NSFetchedResultsController.
So my question is, can I use Regular expression to solve this problem? I don't know much about regular expression. Could anyone give example for this?
You can use predicateWithBlock method for creating your predicate. Inside the block, check whether the dynamicVale contains the value to be searched.
Code:
NSArray *yourArray = ....;
NSString *searchText = ...;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary * _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSArray *dynamicVales = [evaluatedObject valueForKeyPath: #"dynamicVales.value"];
return [dynamicVales containsObject:searchText];
}];
NSArray *filteredArray = [yourArray filteredArrayUsingPredicate:predicate];
Copied from JhonyKutties answer. Enjoy coding :)
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;
}
The query works fine if directly added to a predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"author == %#", author];
[request setPredicate:predicate];
[self.managedObjectContext executeFetchRequest:request error:nil];
The query doesn't work if created and then passed to a predicate
Is there a solution? I rather not pass the predicate itself to the method
Author is a subclass of NSManagedObject
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "%#"'
[self fetchObjectsWithQuery:[NSString stringWithFormat:#"author == %#", author];
- (void)fetchObjectsWithQuery:(NSString *)query
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%#", query];
[request setPredicate:predicate];
[self.managedObjectContext executeFetchRequest:request error:nil];
}
Format strings work differently in
NSString *query = [NSString stringWithFormat:#"author == %#", author] // (1)
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"author == %#", author]
In particular the placeholders "%#" and "%K" have different meanings.
(1) produces a string of the form
"author == <textual description of author object>"
which you cannot use in
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%#", query];
So you cannot pre-format predicates as strings. Another example to demonstrate the problem:
[NSPredicate predicateWithFormat:#"author == nil"]
works, but
[NSPredicate predicateWithFormat:"%#", #"author == nil"]
does not.
There's no good reason not to create and pass around NSPredicate objects. There are ways to do exactly what you want to do, but they are either less expressive than just using predicates or will require you to duplicate the logic underlying NSPredicate.
I need to use a NSPredicate like this:
NSString *authorNameAndLastName = #"Name LastName";
[NSPredicate predicateWithFormat:#"%# beginswith[cd] %K",authorNameAndLastName,#"name"]
but it doesn't work and I receive this exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate ("Name LastName" BEGINSWITH[cd] name).'
After a lot of try I suppose I can't put a keypath (name) in right side of the predicate expression.
It's right?
There's a workaround to obtain what I need in this specific case?
This only would work with arrays and sets. Core Data doesn't support this type of predicate.
Have you tried using [NSPredicate predicateWithBlock:]?
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(YourObject *object, NSDictionary *bindings) {
NSComparisonResult result = [authorNameAndLastName object.name options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [object.name length])];
if (result == NSOrderedSame) {
return YES;
}
return NO;
}];
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.