CompoundPredicate NSPredicate - ios

I have for example three NSPredicates.
NSPredicate *pred1 = [NSpredicate ...];
NSPredicate *pred2 = [NSpredicate ...];
NSPredicate *pred3 = [NSpredicate ...];
And I have from server string how I need combine predicates.
For Example
(1 OR 2 OR 3)
(1 OR 3) AND 2
3 OR (1 AND 2)
How can I combine my three predicates on the right conditions and given the brackets?
Thank you

You can always create nested NSCompoundPredicate like this
NSPredicate *orPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[firstPredicate, secondPredicate]];
NSPredicate *andPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[thirdPredicate, fourthPredicate]];
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[orPredicate, andPredicate]];

One approach is to make a "truth table inputs" from your predicates, make a combined predicate based on the string, and evaluate it using truth table data.
In order for the predicate to work on a truth table you need to convert (1 OR 2) AND 3 to (SELF[1]==YES OR SELF[2]==YES) AND SELF[3]==YES format. You can do it with regular expressions:
NSString *combine = #"(1 OR 2) AND 3";
NSError *err;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:#"\\d+"
options:0
error:&err];
NSString *subst = [regex
stringByReplacingMatchesInString:combine
options:0
range:NSMakeRange(0, combine.length)
withTemplate:#"SELF[$0]==YES"];
NSLog(#"%#", subst);
Next, you can build NSPredicate:
NSPredicate *pred = [NSPredicate predicateWithFormat:subst];
Finally, you need to build your truth table. Evaluate predicates one by one, and place the results into NSArray. Place an unused element at index zero, because your substitution strings use one-based indexing.
Now you are ready to use the predicate that you prepared earlier:
NSArray *data1 = #[#(NO), #(YES), #(NO), #(YES)];
NSArray *data2 = #[#(NO), #(NO), #(NO), #(YES)];
NSLog(#"%d", [pred evaluateWithObject:data1]); // Prints 1
NSLog(#"%d", [pred evaluateWithObject:data2]); // Prints 0
I used fixed values for my truth tables data1 and data2. You would need to evaluate predicates pred1..predN, and put the results into positions 1..N of the NSArray.

you can use this code ex:
NSPredicate *p1 = [NSPredicate ];
NSPredicate *p2 = [NSPredicate "];
NSPredicate *p3 = [NSPredicate "];
in final predicate
NSPredicate *p = [NSCompoundPredicate andPredicateWithSubpredicates: #[p1, p2,p3]];
this work like (p1 AND p2 AND p3)
you can also use orPredicateWithSubpredicates,notPredicateWithSubpredicate with nscompoundpredicate

Related

NSString as float in CoreData predicate

I have CoreData Table with NSString field. But in the filter I need to be interpreted as float.
fieldName.floatValue does not give the correct result. If any way to do this without changing the field type in the table ?
Edit:
Data is:
company = "CompanyName";
date = "2016-01-17";
value = "379.76";
My predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"value.floatValue > 2000"];
But value 379.76 enters the result. Using value.floatValue all the same values are compared as strings.
TNX
If you know value is not negative, you can use this trick:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"abs:(value) > 2000"];
The abs: forces SQLite to treat the value attribute as a number.
Alternatively, add zero to it (which works for negative numbers):
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"add:to:(value,0) > 2000"];
I think you should change String type to Number, this is the easiest way.
You can use this predicate:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [[evaluatedObject value] floatValue] > 2000; // Change id to your Class
}];
But this predicate can not be used with CoreData. So you should store the fetch result to an array, then use this predicate with it.
//Try with this.
// '>' will apply when 'value' is number. So, I first put your values in an array. Only then, I can apply the predicate.
NSString *company = #"CompanyName";
NSString *date = #"2016-01-17";
NSString *value = #"379.76";
NSArray *dataArray = #[#(value.floatValue),#(379.76),#(379),#(200)];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self > %f",200.0];
NSArray *array = [dataArray filteredArrayUsingPredicate:predicate];
//output:#(379.76),#(379.76),#(379)
Try this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"value > 2000"];

NSPredicate to compare identical string arrays?

Is it possible to use nspredicate to compare whether one NSArray is exactly equal to another NSArray of strings? I need this dome via predicates because of its possible I will add this predicate to a compound predicate.
The Array I am comparing against is a property of an NSDictionary.
So the answer was a mixture of both, I did use the predicatewithformat but got creative in the string inside, inspired by #akashivskyy and #avi
[predicatesArray addObject:[NSPredicate predicateWithFormat:#"ANY dynamic.values == %#", arr]];
Edit: As (partially) suggested by Avi, you may use the equality predicate:
NSArray *otherArray = #[ #"foo", #"bar" ];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self == %#", otherArray];
[predicate evaluateWithObject:#[ #"foo", #"bar" ]]; // YES
[predicate evaluateWithObject:#[ #"baz", #"qux" ]]; // NO
Alternatively, and if you have any trouble with format string in the future, you may always use a block predicate to perform your own logic:
NSArray *otherArray = #[ #"foo", #"bar" ];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^(NSArray *evaluatedArray, NSDictionary<NSString *, id> *bindings) {
return [evaluatedArray isEqualToArray:otherArray];
}];
// use the predicate

NSPredicate not filtering as desired

Consider the following array:
NSArray *dataValues = #[#"Foo[0]", #"Foo[1].bar"];
And the following regex pattern, predicate and expected output:
NSString *pattern = #"Foo[0]";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF BEGINSWITH[cd] %#", pattern];
NSArray *results = [dataValues filteredArrayUsingPredicate: predicate];
NSLog(#"matches = %ld", (long)results.count);
This prints 1 in the console as expected. If we change the pattern to:
NSString *pattern = #"Foo\\[[0-9]\\]";
I would expect this to print 2 in the console, but it prints 0. I have double escaped the outer square brackets to allow them to be parsed and expect to find strings that have the numbers 0 to 9 inside the brackets to match this expression.
I have checked the regex against the following site, which does work correctly:
http://regexr.com/3bcut
I have no warnings/errors in Xcode (6.4, 6E35b) running against the iOS 8.4 iPhone 6 Plus simulator, but why does my regex not filter as expected?
You could try this depending on what your needs are:
NSArray *dataValues = #[#"Foo[0]", #"Foo[1].bar"];
NSString *pattern = #"Foo[*]*";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF LIKE %#", pattern];
NSArray *results = [dataValues filteredArrayUsingPredicate: predicate];
NSLog(#"matches = %ld", (long)results.count);
You could go a little more basic and use
NSMutableArray *results = [NSMutableArray array];
for (NSString *str in dataValues) {
if ([str rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]].location != NSNotFound) {
if ([str hasPrefix:#"Foo["]) {
[results addObject:str];
}
}
}
NSLog(#"matches = %ld", (long)results.count);
After raising a TSI with Apple (well, who uses those things anyway?) they said I simply needed to use MATCHES instead of BEGINSWITH, which is only used for string matching - whereas I am trying to match on a regex.
My predicate should have therefore read:
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF MATCHES[cd] %#", pattern];

Writing an NSPredicate for 'CONTAINED_BY' text query

I am trying to construct a query of a Core Data store which retrieves an entity's attribute values when they occur in longer string;
i.e, instead of seeking instances where attribute value contains a (shorter) string :
request.predicate = [NSPredicate predicateWithFormat:#"carBrand contains[c] 'merced'"]
I want to find instances (of the entity) whose attribute values are found 'contained in' an arbitrary (longer) string :
NSString* textString = #"Elaine used to drive Audis, but now owns a Mercedes";
request.predicate = [NSPredicate predicateWithFormat:#"%# contains[c] carBrand", textString ];
(ie. retrieve array holding objects with carBrand = #"Audi" and carBrand = #"Mercedes")
In my attempts, NSPredicate doesn't seem to like expressions with the attribute name on the right hand side and throws an error...
[__NSCFConstantString countByEnumeratingWithState:objects:count:]:
unrecognized selector sent to instance 0x
...is there a way of constructing such a query with the attribute name on the left hand side - a 'contained_by' query, as it were?
PS. Searching SO, I've only found solutions by splitting the text into component words which, in my scenario, would be less than ideal! Is this the only type of approach that's viable?
Build a regex string with your array and use MATCHES in your predicate.
[NSPredicate predicateWithFormat:#"%# MATCHES '*(Audi|Mercedes)*'", testString];
To filter cars based on their brand:
NSArray *brands = [#"Audi", #"Mercedes"];
[NSPrediate predicateWithFormat:#"carBrand IN %#", brands];
Decided to try implementing a componentsSeparatedByString approach to build a NSCompoundPredicate
//find alphabetic words omitting standard plurals
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(?:[^a-z]*)([a-z]+?)(?:s)?\\W+" options:NSRegularExpressionCaseInsensitive error:nil];
//separate with pipe| and split into array
NSString *split = [regex stringByReplacingMatchesInString:textString options:0 range:NSMakeRange(0, speciesString.length) withTemplate:#"$1|"];
NSArray *words = [split componentsSeparatedByString:#"|"];
//build predicate List
NSMutableArray *predicateList = [NSMutableArray array];
for (NSString *word in words) {
if ([word length] > 2) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"brandName beginswith[c] %#", word];
[predicateList addObject:pred];
}
}
//CarBrand* object;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"CarBrand" inManagedObjectContext:self.managedObjectContext];
request.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicateList];
NSError *error =nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
This retrieves the instances found in the text;
eg1:
#"Elaine used to drive Audis, but now owns a Mercedes";
gives array of objects whose .brandname = "Audi", "Mercedes", respectively .
eg2: #"Among the cars stolen were a Ford Mondeo, a Fiat 500C and an
Alfa-Romeo Spyder"
yields .brandname = "Ford", "Fiat",and "Alfa Romeo"(NB no '-') respectively.
I'm not yet accepting my own answer as it seems too much of a workaround and won't easily extend to (for example) extracting brand-name AND, say, model.
Hopefully, someone will have a better solution!

How to filter NSArray using two NSPredcates

I would like to filter an NSArray of NSDictionaries, however I would like to filter the result using one, two or even three NSPredicate values?
Currently I am filtering my array by doing this.
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"parts == %#", filterString];//keySelected is NSString itself
NSMutableArray *filteredArray = [NSMutableArray arrayWithArray:[currentParts filteredArrayUsingPredicate:predicateString]];
sortedItemsArray = [filteredArray mutableCopy];
But I am not sure how I would do this using two predicates?
The other two predicates individually look like the one above accept different keys.
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"area == %#", filterString];
and
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"item == %#", filterString];
What I was thinking is that maybe you could have something like
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"stage == %# area == %#", filterString, areaflterstring];
But I don't think that's possible.
It is possible, but you need to tell the predicate how to combine the parts, like:
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"stage == %# AND area == %#", filterString, areaflterstring];
You can alternatively use NSCompoundPredicate to combine a number of predicates.

Resources