Using of ALL/ANY/NONE in NSPredicate - ios

I'm trying to understand NSPredicate and it's syntax, when we are using ALL/ANY etc
My class Person:
#interface Person : NSObject
#property (nonatomic, strong) NSString* name;
#property (nonatomic, strong) NSString* surname;
#property NSUInteger age;
#property NSUInteger weight;
#property NSUInteger height;
And I use it for predicates like this:
NSArray* persons = [NSArray arrayWithObjects:personOne, personTwo, personThree, personFour, nil];
My predicate:
NSPredicate* predicateEight = [NSPredicate predicateWithFormat:#"ANY SELF.age > %#", #55];
filteredArray = [persons filteredArrayUsingPredicate:predicateEight];
NSLog(#"Age > 55 %#", filteredArray);
And I've received a mistake:
The left hand side for an ALL or ANY operator must be either an
NSArray or an NSSet
How I need to change my code?
Thanks in advance for any help.

It should like this :
NSPredicate* predicateEight = [NSPredicate predicateWithFormat:#"self.age > %d", 55];

;)
ALL/ANY used for NSArray or NSSet. In your case ANY used for instance of Person.
Example of use ANY:
NSPredicate* predicateEight = [NSPredicate predicateWithFormat:#"ANY SELF.age > %#", #55];
BOOL result = [predicateEight evaluateWithObject:persons];
If you have Array of persons (Array of Arrays):
NSArray* group1 = [NSArray arrayWithObjects:personOne, personTwo, personThree, nil];
NSArray* group2 = [NSArray arrayWithObjects:personFour, personFive, personSix, nil];
NSArray* group3 = [NSArray arrayWithObjects:personSeven, personEight, personNine, nil];
NSArray* groups = [NSArray group1, group2, group3, nil];
NSPredicate* predicateEight = [NSPredicate predicateWithFormat:#"ANY SELF.age > %#", #55];
NSArray* filteredArray = [groups filteredArrayUsingPredicate:predicateEight];

Related

String contains any element of an Array [duplicate]

I know I can check if a string contains another string like this
NSString *string = #"hello bla bla";
if ([string rangeOfString:#"bla"].location == NSNotFound) {
NSLog(#"string does not contain bla");
} else {
NSLog(#"string contains bla!");
}
But what if I have an NSArray *arary = #[#"one",#"two", #"three", #"four"] and I wanted to check if a string contains either one of these without just loop or have a bunch of or's (|| ). So it would be something like this
if (array contains one or two or three or four) {
//do something
}
But if I have a longer array this becomes tedious so is there another way, without just looping through?
EDIT
I want to check if myArray has any of theses values in valuesArray
valuesArray =#[#"one",#"two", #"three", #"four"];
myArray = [#"I have one head", #"I have two feet", #"I have five fingers"]
OUTPUT
outputArray = #[#"I have one head", #"I have two feet"]
There you go:
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id __nonnull evaluatedObject, NSDictionary<NSString *,id> * __nullable bindings) {
for(NSString* val in valuesArray) {
if ([evaluatedObject rangeOfString:val].location != NSNotFound)
return true;
}
return false;
}]];
arrRet contains exactly the two desired strings.
A little bit more magic later you have your code without writing a loop :P
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *,id> * bindings) {
BOOL __block match = false;
[valuesArray enumerateObjectsUsingBlock:^(id __nonnull obj, NSUInteger idx, BOOL * __nonnull stop) {
*stop = match = [evaluatedObject rangeOfString:obj].location != NSNotFound;
}];
return match;
}]];
You could use a NSCompoundPredicate
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Where your subPredicates must look like
(
SELF CONTAINS[c] "one",
SELF CONTAINS[c] "two",
SELF CONTAINS[c] "three",
SELF CONTAINS[c] "four"
)
To get there from
NSArray *array = #[#"one", #"two", #"three", #"four"]
You could use a for loop, but as you are opposed to that, let's cheat:
by using a category I each NSArray functional mapping, but instead of looping, I use enumerating
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
Now I can create the subPredicates like
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
and create the compound predicate like
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
and use it
BOOL doesContain = [predicate evaluateWithObject:string];
et voilĂ : No (obvious) looping, while there is one hidden in the enumeration and probably in the predicate as-well.
Now with the changed question you basically ask for filtering. You can use the same predicate for that:
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
results now contains
(
I have one head,
I have two feet
)
the complete code
#import <Foundation/Foundation.h>
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
}
return 0;
}
Besides my cheating the my other question, here an idea how really to avoid time costly looping: Use Set computation magic!
created a class 'Sentence', instantiate it with the strings to test
created a 'Word' class that takes a word to search for
overwrite both classes' isEqual: method to match for if a word is in the sentence (use sets there too!)
put those into an array.
from this array create a NS(*)Set object
put all word in a set
execute union on it.

How to filter custom object in objective-C

I have a custom object which contains data as:
#interface Students : NSObject
{
}
#property (nonatomic,strong) NSString *ID;
#property (nonatomic,strong) NSString *FirstName;
#property (nonatomic,strong) NSString *MiddleName;
#property (nonatomic,strong) NSString *LastN
I want to filter it according to the last name. For example:
I want an array which contains all the details of students having last name="Cena"
I tried this:
NSMutableArray *arrayToFilter = self.studentList;
NSString *nameformatString = #"LastName contains[c] a";
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:nameformatString];
[arrayToFilter filterUsingPredicate:namePredicate];
When I run my app I am getting this error and my app crashes:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
'[< Students 0x7fd25e102d20> valueForUndefinedKey:]:
this class is not key value coding-compliant for the key a.'
I am only able to get last name in array but I know this is wrong. How can I sort the custom object based on last name.
Code I am using to get lastName:
names = [[NSMutableArray alloc]init];
for (int i = 0; i < [self.studentList count] ; i++)
{
Students * studentsObj = (Students*)[self.studentList objectAtIndex:i];
[names addObject:studentsObj.LastName];
}
Update the predicate to ,
NSString *textToMatch = #"a";
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:#"LastN CONTAINS[cd] %#", textToMatch];
then
NSMutableArray *arrayToFilter = self.studentList;
NSArray *filteredArray = [arrayToFilter filteredArrayUsingPredicate:namePredicate];
for getting last name only,
NSArray *lastNames = [filteredArray valueForKey:#"LastN"];
NSArray *sortedArray = [lastNames sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];

check for a key within an object within an array of objects [duplicate]

I have a NSArray of Contact objects, we can call it contacts. Contact is a superclass, FacebookGroup and Individual are subclasses of Contact. FacebookGroup has a property called individuals which is a set of Individual objects.
I also have a NSArray of NSString objects, we can call it userIDs.
What I want to do is create a new NSArray from the existing contacts array that match the userIDs in userIDs.
So if contacts has 3 Contact objects with userID 1,2 and 3. And my userIDs has a NSString object 3. Then I want the resulting array to contain Contact which equals userID 3.
Contact.h
Contact : NSObject
FacebookGroup.h
FacebookGroup : Contact
#property (nonatomic, strong) NSSet *individuals;
Individual.h
Individual : Contact
#property (nonatomic, strong) NSString *userID;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userId = %#", myContact.userId];
NSArray *filteredArray = [contacts filteredArrayUsingPredicate:predicate];
Is this what you are looking for?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userID IN %#", userIDs];
NSArray *filtered = [contacts filteredArrayUsingPredicate:predicate];
i'm expecting you want like this once see this one,
NSMutableArray *names = [NSMutableArray arrayWithObjects:#"one", #"two", #"three", #"four", nil];
NSMutableArray *ids = [NSMutableArray arrayWithObjects:#"1", #"2", #"2", #"3", nil];
NSMutableArray *array=[[NSMutableArray alloc]init];
for(int i=0;i<[ids count];i++){
if([[ids objectAtIndex:i] isEqualToString:#"2"])
[array addObject:[names objectAtIndex:i]];
}
NSLog(#"%#",array);
O/P:-
(
two,
three
)

Search NSArray of objects for String matching any property

I have an NSArray of objects, and those objects have 10 properties. I would like to do a text search of those objects.
I know how to search 1 property at a time, but is there an easy way to search ALL properties at once?
Here is a list of properties that my objects have:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * phone;
#property (nonatomic, retain) NSString * secondaryPhone;
#property (nonatomic, retain) NSString * address;
#property (nonatomic, retain) NSString * email;
#property (nonatomic, retain) NSString * url;
#property (nonatomic, retain) NSString * category;
#property (nonatomic, retain) NSString * specialty;
#property (nonatomic, retain) NSString * notes;
#property (nonatomic, retain) NSString * guid;
If I search for "doctor", I would like to see all results where 1 or more of these properties has the word "doctor" in it. For example, if 1 object has a category of "doctor", and another object has an email address of "smith#doctorsamerica.com", they should both show up in the results.
NSString *searchTerm = #"search this";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF LIKE[cd] %#", searchTerm];
NSArray *filtered = [array filteredArrayUsingPredicate:predicate];
If there is a specific property you can change the predicate to:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.propertyName LIKE[cd] %#", searchTerm];
To search all properties, you will have to bind them together with a logical operator
NSString *query = #"blah";
NSPredicate *predicateName = [NSPredicate predicateWithFormat:#"name contains[cd] %#", query];
NSPredicate *predicatePhone = [NSPredicate predicateWithFormat:#"phone contains[cd] %#", query];
NSPredicate *predicateSecondaryPhone = [NSPredicate predicateWithFormat:#"secondaryPhone contains[cd] %#", query];
NSArray *subPredicates = [NSArray arrayWithObjects:predicateName, predicatePhone, predicateSecondaryPhone, nil];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Add a matches: method to your class:
- (BOOL)matches:(NSString *)term {
if ([self.name rangeOfString:term options:NSCaseInsensitiveSearch| NSDiacriticInsensitiveSearch].location != NSNotFound) {
return YES;
} else if ([self.phone rangeOfString:term options:NSCaseInsensitiveSearch| NSDiacriticInsensitiveSearch].location != NSNotFound) {
return YES;
} else if (...) { // repeat for all of the properties
return YES;
}
return NO;
}
Now you can iterate over your objects checking each one:
NSArray *peopleArray = ... // the array of objects
NSString *someTerm = ... // the search term
NSMutableArray *matches = [NSMutableArray array];
for (id person in peopleArray) {
if ([person matches:someTerm]) {
[matches addObject:person];
}
}
Instead of hardcoding each property name individually, you could get an array of the class's property names: List of class properties in Objective-C, (assuming they were all strings, or you could check if each property's type is of class NSString but I'm not sure how to do this)
and then on each of the objects you'd like to search, you could iterate through each of the object's properties and inspect the value on each:
id objectValue = [object valueForKey:[NSString stringWithUTF8String:propertyName]];
// let's say the property is a string
NSString *objectValueString = (NSString *)objectValue;
Then you could check if the property matches your searchTerm with something like:
BOOL propertyValueMatchesSearchTerm = [objectValueString rangeOfString:mySearchTermString].location != NSNotFound;
if (propertyValueMatchesSearchTerm) {
// add object to search results array
}

Filter NSArray of NSNumber with NSArray of NSNumber with NSPredicate

I have this custom class:
#interface MyModel : NSObject
#property (nonatomic,strong) NSString *id_name;
#property (nonatomic,strong) NSArray *genres;
#end
The genres array is an array of NSNumbers. I fill another array with the MyModel object, for example:
MyModel *m = [[MyModel alloc] init];
m.id_name = #"2345";
m.genres = [NSArray arrayWithObjects:[NSNumber numberWithInt:3],[NSNumber numberWithInt:5],nil];
MyModel *m2 = [[MyModel alloc] init];
m2.id_name = #"259";
m2.genres = [NSArray arrayWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:10],nil];
MyModel *m3 = [[MyModel alloc] init];
m3.id_name = #"25932as";
m3.genres = [NSArray arrayWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:10],[NSNumber numberWithInt:15],nil];
myArray = [NSArray arrayWithObjects:m,m2,m3,nil];
Now I want to filter myArray such that the genres are contained within the elements of this array:
NSArray *a = [NSArray arrayWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:10],nil];
So, myArray, after filtering, should contain the objects m2 and m3. Can I do this with NSPredicate? If so, how? Or is there another way?
Martin R has provided an elegant answer, but if I understand your question correctly, you can also use a simpler predicate:
NSPredicate *allPred = [NSPredicate predicateWithFormat: #"ALL %# IN genres", a];
NSArray *result = [myArray filteredArrayUsingPredicate: allPred];
This predicate will find MyModels that contain all of the genres included in your a array. In the case of your test data, that will be the two objects with ids 259 and 25932as.
To find the objects that have at least one genre in the given array a, use
[NSPredicate predicateWithFormat:#"ANY genres in %#", a];
To find the objects that have all genres in the given array, a SUBQUERY is needed:
[NSPredicate predicateWithFormat:#"SUBQUERY(genres, $g, $g IN %#).#count = %d", a, [a count]];
(The idea is to check if the number of genres that are in the given array is equal
to the size of the array.)

Resources