NSPredicate does not return the expected items - ios

I am trying to find an items in the array where itemId and size are not matched.
However, the following query does not return me item(s) where itemId same but size different. I wonder what I am missing.
First I am extracting the item as follows, then I am trying to find compliment of this item.
NSPredicate *itemPredicate = [NSPredicate predicateWithFormat:#"(%K == %#)", kItemId, itemId];
NSPredicate *sizePredicate = [NSPredicate predicateWithFormat:#"(%K == %#)", kSize, [NSString stringWithFormat:#"%d", size]];
NSPredicate *combinedPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[itemPredicate, sizePredicate]];
NSArray *itemInOrder = [sharedData.orderItems filteredArrayUsingPredicate:combinedPredicate];
Trying to extract complimentary items of itemInOrder
NSPredicate *restOfOrderPredicate = [NSPredicate predicateWithFormat:#"(%K != %#)", kItemId, itemId];
NSPredicate *restSizePredicate = [NSPredicate predicateWithFormat:#"(%K != %#)", kSize, [NSString stringWithFormat:#"%d", size]];
NSPredicate *restCombinedPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[restSizePredicate, sizePredicate]];
NSMutableArray *restOfItemsInOrder = [NSMutableArray arrayWithArray:[sharedData.orderItems filteredArrayUsingPredicate:restOfOrderPredicate]];

I think there are 2 issues here:
Your complementary and predicate is using the sizePredicate, but I think you want to be using restSizePredicate.
I think you want to use an or predicate. As you have it now, you'll miss anything that has just the same size or just the same itemId.
An alternate approach could be to use a fetch that gets all the objects. Then use your existing fetch to get the original set you wanted. Then use Set Arithmetic to remove the second set from the first.
** EDIT **
I just now discovered notPredicateWithSubpredicate which is probably exactly what you want: Keep your first query and predicate as is. Then for the second query, use notPredicateWithSubpredicate and pass in your predicate from the first query.

Related

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).

use NSPredicate to filter object

Here say I have a array of objects with two attributes:
// array of object
NSArray *objects
// object
NSString *primaryTag;
NSArray *secondaryTag;
Since what I want is when the this object contains the givenTag, it could be passed to a new array called results;
Here is my codes:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
results = [objects filteredArrayUsingPredicate:resultPredicate];
It seems that the primaryTag works well, but the secondaryTag doesn't work, can someone help me out. I am not that familiar with NSPredicate filtering. Thanks in advance.
The most efficient way to do that is with a NSCompoundPredicate like so:
NSArray *subPredicates = #[tag1, tag2, tag3];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Your question is a little unclear so you might also want:
andPredicateWithSubpredicates
Depending on exactly what the nature of the result set you are looking for.
See Apple Docs here: NSCompoundPredicate Docs
i implemented the following custom class:
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString *primaryTag;
#property (strong, nonatomic) NSArray *secondaryTag;
#end
and overrode it's description method for the NSLog statement to print something we understand:
- (NSString *)description {
return [NSString stringWithFormat:#"primaryTag: %#, secondaryTag: %#", _primaryTag, [_secondaryTag componentsJoinedByString:#", "]];
}
then i created some objects from the custom class and added them to an array:
NSMutableArray *objects = [NSMutableArray array];
CustomObject *obj1 = [CustomObject new];
obj1.primaryTag = #"stringToSearchFor";
obj1.secondaryTag = #[#"notTheStringToSearchFor", #"somethingElse"];
[objects addObject:obj1];
CustomObject *obj2 = [CustomObject new];
obj2.primaryTag = #"differentString";
obj2.secondaryTag = #[#"nothingWeAreLookingFor"];
[objects addObject:obj2];
CustomObject *obj3 = [CustomObject new];
obj3.primaryTag = #"anotherOne";
obj3.secondaryTag = #[#"whoCaresForThisString", #"stringToSearchFor"];
[objects addObject:obj3];
finally i created a string to search for and the predicate:
NSString *givenTag = #"stringToSearchFor";
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
when i log out the result i get the correct results:
NSLog(#"%#", [objects filteredArrayUsingPredicate:resultPredicate]);
logs:
(
"primaryTag: stringToSearchFor, secondaryTag: notTheStringToSearchFor, somethingElse",
"primaryTag: anotherOne, secondaryTag: whoCaresForThisString, stringToSearchFor"
)
which is obj1 and obj3. correct! if it does not work for you there's gotta be something else wrong with your code...
If my understanding of the original question is incorrect, please let me know, and I will adjust my answer.
Problem: You have an array of objects with 2 properties. One is primaryTag, which is a string. The second is an array of secondaryTags, which is a collection of strings. You want to filter all objects where either the primaryTag matches, or where the search string matches one of the secondaryTags.
Answer The proper way to match strings is via MATCHES or CONTAINS.
NSPredicate *pPredicate =
[NSPredicate predicateWithFormat:#"%K CONTAINS[cd] %#",
#"primaryTag", searchString];
NSPredicate *sPredicate =
[NSPredicate
predicateWithFormat:#"SUBQUERY(%K, $st, $st CONTAINS[cd] %#).#count > 0",
#"secondaryTags", searchString];
NSCompoundPredicate *searchPredicate =
[NSCompoundPredicate orPredicateWithSubPredicates:#[ pPredicate, sPredicate ]];
How it works: The first predicate is a straightforward match. You can replace CONTAINS with MATCHES, if that better fits the kind of comparison you wish to make. The [cd] suffix means case-insensitive and diacritic-insensitive. It's normal to include those when searching/filtering, but again, it's up to you. Instead of embedding the property name in the predicate format string, I use %K and a replacement parameter. In production code, that replacement parameter would be a constant.
The second predicate is a little trickier. It uses a SUBQUERY() to filter the secondaryTags array, and returns the object as matching if at least one secondary tag matches the search string. SUBQUERY() is a function with 3 parameters. The first is the collection being searched. The second is a temporary variable that represents each item in the collection, in turn; it is used in the 3rd parameter. The 3rd parameter is a regular predicate. Each item in the collection that matches the filter is included in the output of SUBQUERY(). At the end, the matching secondary tags are counted (via #count), and if the count is greater than zero, the original object is considered to have matched, so will be included in the filtered output.
Finally, we combine these two predicates into one searchPredicate, which can now be used to filter your array of objects.
I seen this issue,
My normal approch is to use the NSPredicate twice,
So that I can track the result at every steps:
Option 1:
NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"primaryTag == %#", givenTag];
results1 = [objects filteredArrayUsingPredicate:resultPredicate1];
NSPredicate *resultPredicate2 = [NSPredicate predicateWithFormat:#"secondaryTag CONTAINS[c] %#", givenTag];
finalResults = [results1 filteredArrayUsingPredicate:resultPredicate2];
Option 2:
Use NSCompoundPredicate to compound multiple filtering. You can easily find many examples on google and stackOverFlow.
Hope this will help,
Thanks

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.

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.

NSPredicate possibly matching multiple properties

I have a Car class with instance variables such as "color" and "make".
I need a NSPredicate that allows me to search for either color or make or both.
NSPredicate*predicate=[NSPredicate predicateWithFormat:#"(color contains[c] %#) AND (make contains[c] %#)", chosenColor,chosenMake];
This predicate requires that there is BOTH a color and a make. If the user only gives a color, no results will be returned because the make then will be "nil". No cars has nil for any instance variables.
Also, the search will be for many variables, not only color and make, so an if/case situation is not wanted. Is there any options that gives my the possibility to search for "AND if NOT nil". I will appreciate any help.
You can build your predicate format dynamically to test only non-nil attributes. More on that here. Also consider making your search diacritic-insensitive (adding a 'd' to your CONTAINS statement). Take "Škoda" for example. You want people to find it with "skoda" as well.
This is pretty easy with the NSCompoundPredicate API:
NSString *chosenColor = ...;
NSString *chosenMake = ...;
NSMutableArray *subpredicates = [NSMutableArray array];
if (chosenColor != nil) {
NSPredicate *p = [NSPredicate predicateWithFormat:#"color contains[cd] %#", chosenColor];
[subpredicates addObject:p];
}
if (chosenMake != nil) {
NSPredicate *p = [NSPredicate predicateWithFormat:#"make contains[cd] %#", chosenMake];
[subpredicates addObject:p];
}
NSPredicate *final = nil;
if ([subpredicates count] > 0) {
final = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
}

Resources