NSPredicate format evaluateWithObject with nsarray containing nsdictionaries - ios

Is there any cookbook for NSPredicate format? I'm wonder how to test NSArray with dictionaries, if one of them contains a value "adult" for key "type" as shown in an example below:
NSDictionary* passengers = #[#{#"type":#"child"},#{#"type":#"adult"}];
NSPredicate* testPredicate = [NSPredicate predicateWithFormat:#"ANY SELF.type LIKE[c] adult"];
BOOL adultOnBoard = [testPredicate evaluateWithObject: passengers];
DO NOT MIX WITH filtering array - I expect testing as a "cheap" operation comparing to filtering.

Related

Filter NSArray of strings when portion of string is empty using NSPredicate

I would like to filter out items of an array of strings where the right portion of a string is empty using NSPredicate but my knowledge of NSPredicate is limited.
The strings look should like {"0,General", "1,Sports", "2,Entertainment"} etc.
I want to filter out cases where you have something like: "0," or even ","
So if I had {"0,General", "1,Sports", "0,","2,Entertainment"}, I would want to filter out the flawed category "0,".
I know I can filter out entirely empty strings with something like the following:
NSArray *filteredcats = [mycats filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"length > 0"]];
How can I filter out strings in the array where anything to the right of a comma in the string is empty?
You can easily use regular rexpressions for this,
NSArray *myArray = #[#",First",#"0,General",#"1,Sports",#"2,Entertainment",#"3,",#","];
NSString *regex = #"^\\d,\\w+$";
NSPredicate *filter = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
NSArray *matches = [myArray filteredArrayUsingPredicate:filter];
NSLog(#"matches: %#", matches);
matches: (
"0,General",
"1,Sports",
"2,Entertainment"
)

Filter NSArray of Arrays by One Element of Array Using NSPredicate in IOS

It is possible to filter an array of strings as follows:
NSArray *array = #[#"honda",#"toyota",#"ford"];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#",#"ford"];
NSArray *filtered = [array filteredArrayUsingPredicate:pred];
I want to search an array that contains arrays of two strings by the values for the first of the strings. So for:
NSArray *cars = #[#[#"honda",#"accord"],#[#"toyota",#"corolla"],#[#"ford",#"explorer"]];
I want to search the first dimension (honda, toyota, ford) for #"ford"
Is there a way to tell the predicate I want to search on only the first attribute and return matching elements of the array?
Well here is the pred you need.
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF[FIRST] contains[cd] %#", #"ford"];

Order a pre-defined string array in order

In the following reversedArray has three or more strings such as Salads, Meats Appetizer in order.
However, I want to have Meats always to be the first string in the array.
NSPredicate *predicateMain = [NSPredicate predicateWithFormat:
#"(%K == %#)", #"categoryType", #"main"];
NSPredicate *predicateSide = [NSPredicate predicateWithFormat:
#"(%K == %#)", #"categoryType", #"side"];
NSPredicate *orPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:
[NSArray arrayWithObjects:predicateMain, predicateSide,nil]];
NSArray *filteredArray = [foods filteredArrayUsingPredicate:orPredicate];
NSArray *reversedArray = [[[filteredArray valueForKeyPath:
#"#distinctUnionOfObjects.categoryName"]
reverseObjectEnumerator] allObjects];
I can do it via hardcode but I want to know proper way of handling.
To avoid hardcoding you could write a function to reorder the array with any given string as the first string, and utilize that.
For example:
void yourFunctionName(string firstString, NSArray &array){
//Iterate through array
//Check if you've found a string matching firstString
//Put it at the front by moving everything else down one
//Continue to iterate until you've reached the end of the array
}
Here it would be best to pass the array by reference (using the &) so that you modify the array itself and not just a copy of the array values (what you get when you don't pass by reference).

NSPredicate nested dictionary dynamic key

I am using core data to fetch a list of objects. These objects have a property named 'categories'. This categories property is a NSDictionary constructed from json like :
#{#"A":#YES, #"B":#NO, #"C":#YES}
I want to get all core data objects which category is true for a given category key.
I tried :
NSString *categoryKey = #"A";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"categories.%K == YES", categoryKey]];
// or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.categories.%# == YES", categoryKey]];
// or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"categories[%#] == YES", categoryKey];
// Throws exception : Unsupported function expression categories["A"]
// or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"categories[%K] == YES", categoryKey];
// Throws exception : Unsupported function expression categories[A]
// or
NSString *categoryKeyPath = [NSString stringWithFormat:#"categories.%#", categoryKey];
NSPredicate *p = [NSPredicate predicateWithFormat:#"%K == YES", categoryKeyPath]];
But it always returns empty array when performing my fetch (of course I have some objects with categories[#"A"] = YES).
I think the problem comes from nested dictionary key path but I can't find a way to achieve this with one predicate.
Edit:
To clarify I would use
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [[evaluatedObject.categories objectForKey:categoryKey] boolValue];
}];
But predicateWithBlock: is not supported when fetching in core data.
If I understand correctly categories is a string attribute on your entity, which contains JSON data, and your managed objects parse this JSON so it can be read as a dictionary.
The problem is that the managed objects are not created before the fetch is performed. At that point, categories is just a string, and no key path will be able to extract the info you want from it. You will need to fetch all the objects so they can construct their dictionaries, then filter that collection. For large data sets, this may be slow.
Core Data is designed to be used by explicitly modelling the data; if you jam your own data structure inside a string, Core Data can’t help you. The more idiomatic design would be to have a Categories entity with a to-many/to-many relationship with the entity of your object. This makes it trivial to find all the objects in category A by fetching category A then following the relationship.
Try this
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.categories LIKE \"%#\":true", categoryKey]];

Core Data Predicate - Check if any element in array matches any element in another array

I'm trying to use a predicate to filter objects where an intersection exists between two arrays.
The NSManagedObject has an array(Of Strings) attribute named "transmissions". And there is another array(Of Strings) that will contain words to filter by, named "filters".
I'm not sure how to find if any elements in "transmissions" match any element in "filters".
I've tried
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF.transmission in[c] %#",transmissions];
or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY transmission in[c] %#",transmissions];
However, core data fetches no results where there should be some.
Try this.
NSPredicate *predicate = nil;
NSMutableArray *predicates = [NSMutableArray new];
for (NSString *transmission in transmissions) {
[predicates addObject:[NSPredicate predicateWithFormat:#"transmission == %#", transmission]];
}
if (predicates.count > 0)
predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
// make fetch request using 'predicate'
You can use the keyword CONTAINS to check an object exists in a collection.
[NSPredicate predicateWithFormat:#"favouriteFilms CONTAINS %#", film]
I believe the same thing can be achieved with IN by switching the LHS and RHS of the predicate. I have only successfully implemented this functionality using CONTAINS however.

Resources