setting filter predicate in NSArrayController [duplicate] - objective-c

I'm trying to prepare multiple search in my CoreData entity Recipes. There are parameters by which I would like to prepare fetch.
Recipes attributes:
#property (nonatomic, retain) NSNumber * difficulty;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * code; //like identifier
#property (nonatomic, retain) NSNumber * prepTime;
Ingredients is a separate entity with the list of ingredients.
Join entity contains ingredientCode, recipeCode, count.
getArrayOfJoinDataWithIngredients fetches join entity and returns NSArray of codes of recipes which contains some of input ingredient.
Here is my code:
- (IBAction)callRecipeFetch:(id)sender
{
NSString *predicateString = #"";
NSArray *codes = [[NSArray alloc] init]; codes = nil;
if ([paramController.ingredientsForSearchArray count] > 0) {
NSMutableArray *ingredientsArray = [[NSMutableArray alloc] init];
for (Ingredients *ingredient in paramController.ingredientsForSearchArray) {
[ingredientsArray addObject:ingredient.code];
}
MainTabController *mainTabController = [[MainTabController alloc] init];
codes = [mainTabController getArrayOfJoinDataWithIngredients:ingredientsArray];
NSString *ingrSet = [NSString stringWithFormat:#"(code IN %#)", codes];
predicateString = [predicateString stringByAppendingString:ingrSet];
}
NSString *diff;
if ([predicateString isEqualToString:#""]) {
diff = [NSString stringWithFormat:#"(difficulty <= %d)", paramController.diff.selectedSegmentIndex + 1];
}
else diff = [NSString stringWithFormat:#" AND (difficulty <= %d)", paramController.diff.selectedSegmentIndex + 1];
predicateString = [predicateString stringByAppendingString:diff];
NSString *timeString = [NSString stringWithFormat:#" AND (%d =< prepTime) AND (prepTime <= %d)", paramController.rangeSlider.leftValue, paramController.rangeSlider.rightValue];
predicateString = [predicateString stringByAppendingString:timeString];
if (paramController.categoryCode) {
NSString *categoryString = [NSString stringWithFormat:#" AND (inCategory = %#)", paramController.categoryCode];
predicateString = [predicateString stringByAppendingString:categoryString];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat: predicateString];
[resultController findRecipesWithPredicate:predicate];
}
The full predicateString is #"(code IN (\n 1,\n 3\n)) AND (difficulty <= 5) AND (0 =< prepTime) AND (prepTime <= 28800) AND (inCategory = 12)"
Now I have an error in predicate part (code IN %#) when I prepare NSPredicate with code:
NSPredicate *predicate = [NSPredicate predicateWithFormat: predicateString];
ERROR:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "(code IN (
1,
3
)) AND (difficulty <= 5) AND (0 =< prepTime) AND (prepTime <= 28800) AND (inCategory = 12)"'
How to correctly make predicate with IN operator. Thanks for all advices.

use NSCompoundPredicate for your multiple predicates, you can refer NSCompoundPredicate Class Reference
something like this:
NSPredicate * andPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:predicate1,predicate2,predicate3,nil]];

Addition to #Joshua's answer, you can also use NSCompoundPredicate for your OR operations like this.
Obj-C - OR
// OR Condition //
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[predicate1, predicate2]];
Swift - OR
let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate1,predicate2] )
Swift 3 - OR
let predicate1 = NSPredicate(format: "X == 1")
let predicate2 = NSPredicate(format: "Y == 2")
let predicateCompound = NSCompoundPredicate.init(type: .or, subpredicates: [predicate1,predicate2])

It will work for you.
NSPredicate *bPredicate;
bPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %# OR product_price contains[c] %# OR foodescription contains[c] %#",searchText,searchText,searchText];
NSArray *filteredArray = [getObjectsFromServiceArray filteredArrayUsingPredicate:bPredicate];

Related

Search through NSArray of objects with different locale

I have an array of phone number objects, the phone number model is as follows:
#property NSString *hotlineName;
#property NSString *hotlineNameAr;
#property NSString *hotlineNumber;
#property NSString *hotlineImage;
#property NSInteger hotlineID;
#property RLMArray<Tags> *hotlineTags;
when I perform a search, I filter the array if either hotlineName, hotlineNameAr, hotlineNumber and in property hotlineTags: tagName and tagNameAr contain the search text.
I used NSPredicate to filter the array as such:
-(void) searchForText: (NSString *) searchText{
NSString *predicateFormat = #"SELF.%K contains[cd] %#";
NSString *tagFormat = #"ANY SELF.hotlineTags.%K contains[cd] %#";
NSString *searchNameAttribute = #"hotlineName" ;
NSString *searchTagAttribute = #"tagName";
NSString *searchNameAttribute_ar = #"hotlineNameAr" ;
NSString *searchTagAttribute_ar = #"tagNameAr";
NSString *numberAttribute = #"hotlineNumber";
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:predicateFormat, searchNameAttribute, searchText];
NSPredicate *tagPredicate = [NSPredicate predicateWithFormat:tagFormat, searchTagAttribute, searchText];
NSPredicate *namePredicate_ar = [NSPredicate predicateWithFormat:predicateFormat, searchNameAttribute_ar, searchText];
NSPredicate *tagPredicate_ar = [NSPredicate predicateWithFormat:tagFormat, searchTagAttribute_ar, searchText];
NSPredicate *numberPredicate = [NSPredicate predicateWithFormat:predicateFormat, numberAttribute, searchText];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[namePredicate, numberPredicate, tagPredicate, namePredicate_ar, tagPredicate_ar]];
filteredResults = [hotlines_arr filteredArrayUsingPredicate:predicate];
}
and so far it works well, the problem arose when I search with numbers in a different locale, in this instance in Arabic, so used I NSNumberFormatter as such:
// Convert string From Arabic/Persian numbers to English numbers
+(NSString *) convertToEnglishNumber:(NSString *) string {
// NSNumericSearch
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"EN"];
[formatter setLocale:locale];
NSNumber *number = [formatter numberFromString:string];
return [number stringValue];
}
but the problem with NSNumberFormatter is that any leading zero's are ignored in the conversion, so I need an alternative to format the NSString based on locale or search array while taking in consideration locale, the same option in spotlight search.
I also tried formatting as such but it was in vain.
NSString *str = [[NSString alloc] initWithFormat:#"%#" locale:locale, searchText];
NSString *localizedString =[NSString localizedStringWithFormat:#"%#", searchText];
As I couldn't think of any other option and this was a last resort, I ended up replacing the string but I'm very unhappy with answer:
+ (NSString *) replaceStrings: (NSString *) textToEdit{
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"ar"];
for (NSInteger i= 0; i < 10; i++) {
NSString *stringVal = [#(i) stringValue];
NSString *localeString = [#(i) descriptionWithLocale:locale];
textToEdit = [textToEdit stringByReplacingOccurrencesOfString:localeString withString:stringVal];
}
return textToEdit;
}

Filter array of custom objects with a dictionary property

I have an array that holds MyCustomObject objects.
MyCustomObject has 3 properties:
NSString *id;
NSString *name;
NSDictionary *phones;
How do I filter that array by the content of the "phones" property?
All I saw online is:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"phones CONTAINS[c] %#",textField.text];
self.filteredArray = [self.unfilteredArray filteredArrayUsingPredicate:predicate];
But it doesn't help me much...
Thanks
MyCustomObject *value = [[MyCustomObject alloc] init];
for(value in arrayname)
{
NSString *str = [value.phones objectForKey:#"key"];
NSRange r = [str rangeOfString:textField.text options:NSCaseInsensitiveSearch];
if(r.location != NSNotFound)
{
NSLog(#"Match found");
}
}
self.filterArray = [self.unfilteredArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
MyCustomObject *obj = (MyCustomObject*)evaluatedObject;
return ([[[obj.phones objectForKey:#"key"] lowercaseString] rangeOfString:[textField.text lowercaseString]].location != NSNotFound );
}]];

Array filtering using NSPredicate in objective-c gives error

I have an array within objects like below;
NSMutableArray* tableStructure = [[NSMutableArray alloc] init];
TableStructure *structure = [[TableStructure alloc] init];
structure.TableName =#"Client";
structure.Type =#"U";
structure.ColumnName =#"Id";
structure.DataType =#"int";
structure.Length = 2;
TableStructure *structure2 = [[TableStructure alloc] init];
structure2.TableName =#"Client";
structure2.Type =#"U";
structure2.ColumnName =#"Name";
structure2.DataType =#"text";
structure2.Length = 20;
[tableStructure addObject:structure];
[tableStructure addObject:structure2];
NSString *typeFilter = #"U";
NSString *nameFilter = #"sysname";
NSString *tableNameFilter = #"Sync";
NSPredicate *pred = [NSPredicate predicateWithFormat:#"Type == %# and DataType != %# and TableName != %#)", typeFilter, nameFilter, tableNameFilter];
NSArray *filteredArray = [tableStructure filteredArrayUsingPredicate:pred];
When the debugger came to NSPredicete *pred... line, it gives 'Unable to parse the format string "Type == %# and DataType != %# and TableName != %#)"' error.
How can I solve this?
You have an unbalanced closing parenthesis in the predicate string:
#"Type == %# and DataType != %# and TableName != %#)"
HERE ---^
If you remove that it should work.
You simply have an orphaned ")", try this: [NSPredicate predicateWithFormat:#"Type == %# and DataType != %# and TableName != %#", typeFilter, nameFilter, tableNameFilter];
`
Try this,No need of )
NSPredicate *pred = [NSPredicate predicateWithFormat:#"Type == %# and DataType != %# and TableName != %#", typeFilter, nameFilter, tableNameFilter];

NSPredicate of multidimensional NSArray

Given is an NSArray with objects, each of which has an NSArray with floats, stored as NSNumbers.
I am trying to create an NSPredicate to filter my main array based on the float values. So, for instance, how to get all objects that have the value 234.6 +/- 0.8 as one of the floats in the sub-array?
I can do something like this for a one-dimensional NSArray of floats:
float targetFloat = 234.6;
float delta = 0.8;
filterPredicate = [NSPredicate predicateWithFormat: #"myFloat > %f AND myFloat < %f", (targetFloat - delta), (targetFloat + delta)];
filteredArray = [originalArray filteredArrayUsingPredicate: filterPredicate];
But how do I change it for my 2D NSArray with NSNumbers?
You can use "SELF[index]" in a predicate to access specific elements of the sub-array.
The following predicate finds all sub-arrays where the first element is in the
specified range:
float lowValue = 1.5;
float hiValue = 2.5;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF[0] > %f AND SELF[0] < %f", lowValue, hiValue];
NSArray *filtered = [array filteredArrayUsingPredicate:predicate];
If you want to find the sub-arrays that contain any number in the specified range, use
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF BETWEEN %#", #[#(lowValue), #(hiValue)]];
Your predicate can access the object's property that holds the array by name; I've called this list in the example below. Then use the ANY keyword to check all the values in the array, and BETWEEN to find out if those values are within your chosen range.
#import <Foundation/Foundation.h>
#interface Grumolo : NSObject
#property (copy, nonatomic) NSArray * list;
#end
#implementation Grumolo
- (NSString *)description
{
return [NSString stringWithFormat:#"%#: %p, list: %#", NSStringFromClass([self class]), self, [self list]];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
float target = 234;
float delta = 0.8;
NSPredicate * p = [NSPredicate predicateWithFormat:#"ANY list BETWEEN %#", #[#(target-delta), #(target+delta)]];
NSArray * yes = #[#234, #10, #100];
NSArray * yes2 = #[#0, #16, #234];
NSArray * no = #[#1, #2, #3];
Grumolo * g1 = [Grumolo new];
[g1 setList:yes];
Grumolo * g2 = [Grumolo new];
[g2 setList:yes2];
Grumolo * g3 = [Grumolo new];
[g3 setList:no];
NSLog(#"%#", [#[g1, g2, g3] filteredArrayUsingPredicate:p]);
}
return 0;
}
You might also like to try predicateWithBlock:, which would let you express what you're trying to do with a traditional loop over each array, returning YES if you find an element that meets your criteria and NO if you exhaust the list.
NSPredicate * pb = [NSPredicate predicateWithBlock:^BOOL(Grumolo * evaluatedObject, NSDictionary *bindings) {
NSArray * list = [evaluatedObject list];
__block BOOL result = NO;
[list enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL *stop) {
BOOL moreThanLower = (NSOrderedDescending == [obj compare:#(target-delta)]);
BOOL lessThanUpper = (NSOrderedAscending == [obj compare:#(target+delta)]);
if( moreThanLower && lessThanUpper ){
*stop = YES;
result = YES;
return;
}
}];
return result;
}];

Multiple conditions in NSPredicate

How to use multiple conditions with NSPredicate?
I am using this but not getting anything in the returned array .
NSPredicate *placePredicate = [NSPredicate predicateWithFormat:#"place CONTAINS[cd] %# AND category CONTAINS[cd] %# AND ((dates >= %#) AND (dates <= %#)) AND ((amount >= %f) AND (amount <= %f))",placeTextField.text,selectedCategory,selectedFromDate,selectedToDate,[amountFromTextField.text floatValue],[amountToTextField.text floatValue]];
NSArray *placePredicateArray = [dataArray filteredArrayUsingPredicate:placePredicate];
NSLog(#"placePredicateArray %#", placePredicateArray);
The amount and the category can be empty sometimes. How should I construct the NSPredicate?
You can build your placesPredicate using other NSPredicate objects and the NSCompoundPredicate class
Something like :
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"place CONTAINS[cd] %#", placeTextField.text];
NSPredicate *p2 = [NSPredicate predicateWithFormat:#"category CONTAINS[cd] %#", selectedCategory];
NSPredicate *p3 = [NSPredicate predicateWithFormat:#"(dates >= %#) AND (dates <= %#)", selectedFromDate,selectedToDate];
NSPredicate *p4 = [NSPredicate predicateWithFormat:#"(amount >= %f) AND (amount <= %f)", [amountFromTextField.text floatValue],[amountToTextField.text floatValue]];
NSPredicate *placesPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[p1, p2, p3, p4]];
Now, if you are missing category for example you can just use a dummy YES predicate to replace it :
NSPredicate *p2;
if (selectedCategory) {
p2 = [NSPredicate predicateWithFormat:#"category CONTAINS[cd] %#", selectedCategory];
} else {
p2 = [NSPredicate predicateWithBool:YES]
}
I would tend to handle this one piecemeal. That is,
placePredicate = [NSPredicate predicateWithFormat:#"place CONTAINS[cd] %#",placeTextField.text];
NSMutableArray *compoundPredicateArray = [ NSMutableArray arrayWithObject: placePredicate ];
if( selectedCategory != nil ) // or however you need to test for an empty category
{
categoryPredicate = [NSPredicate predicateWithFormat:#"category CONTAINS[cd] %#",selectedCategory];
[ compoundPredicateArray addObject: categoryPredicate ];
}
// and similarly for the other elements.
Note that I don't bother even putting into the array the predicate for category (or anything else) when I know there isn't one.
// Then
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
compoundPredicateArray ];
And if I were planning to do it a lot, I wouldn't use the format method, but keep around the building blocks and just change whatever changes between uses.
Suppose a class Person with attributes "name", "age", "income"
"personArray" is array of class Person
NSPredicate *mypredicate = [NSPredicate predicateWithBlock:^BOOL(personObj Person, NSDictionary *bindings) {
NSNumber *age = personObj.age;
String *name = personObj.name;
NSNumber *income = personObj.income
BOOL result = (age > 20 && name == "Stack" && income >40000);
return result;
}];
NSArray *filteredArray = [personArray filteredArrayUsingPredicate: mypredicate];
For further you can read this article Array Filter Using Blocks
In Swift
let filterArray = personArray.filter
{ person in
let age = personObj.age;
let name = personObj.name;
let income = personObj.income
BOOL result = (age > 20 && name == "Stack" && income >40000);
return result;
}