I have a bit of a hard time writing a predicate for my search functionality and thought you'd be bale to help. So basically I have two arrays of NSNumbers. I want my predicate to satisfy the following:
If a number's integerValue in array A matches any integerValue in array B.
I don't want to use any sort of loop for this solution. Here's what I have so far
ANY integerValue == ANY //how do I pass the entire array here and ask for the integerValue of each member?
The ANY operator will handle that.
Since it is a bit difficult to say from your question which of the arrays is "self" in normal predicate parlance, I'll write it without a self:
NSArray *arrayA = #[#2, #3, #7];
NSArray *arrayB = #[#2, #4, #9];
NSPredicate *pred = [NSPredicate predicateWithFormat: #"ANY %# IN %#", arrayA, arrayB];
Due to the lack of a "self", it will have to be evaluated with nil as the object, but that works fine:
BOOL matched = [pred evaluateWithObject: nil];
If you prefer to have a "self" in the predicate, you can just enter it:
NSPredicate *pred = [NSPredicate predicateWithFormat: #"ANY self IN %#", arrayB];
BOOL matched = [pred evaluateWithObject: arrayA];
The result is the same.
A small conceptual comment
The predicate above evaluates to true if any integer is included in both arrays, which is how I read your question.
This means that, conceptually speaking, you seem to be testing whether two sets of numbers intersect each other. NSSet's method intersectsSet: checks that, so another way to do the test would be to keep your numbers as sets and test for intersection:
matched = [setA intersectsSet: setB];
I know it's not precisely what you asked for (predicates and all) but another way is to use NSArray's - (id) firstObjectCommonWithArray:(NSArray *)otherArray, which would return nil if no common object can be found.
BOOL arraysIntersect = [array1 firstObjectCommonWithArray:array2] != nil;
One caveat though is that it would use its own object equality rules when comparing two objects, meaning if two objects are NSNumber instances, it will compare them using NSNumber's compare: method. But the same goes for the predicate-based solution proposed so far.
Related
I am trying to fetch items that do not have a value of 1 in an NSNumber attribute. Some of the items I want to fetch have never had the attribute set at all and therefore have a NSNull value. How can I fetch items that are not 1 but may be NSNull?
The attribute is an NSNumber and I am also a bit confused about whether I should be searching on 1 or #1.
The following code is excluding NSNull values when I want to include them:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"&hasserverid!=1"];
Thanks for any suggestions.
You can explicitly test for both cases with an OR:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"hasserverid != YES OR hasserverid == NULL"];
By the name of your property, I assume when you say 1 you're actually referring to the value true. Using YES in the predicate format instead of 1 makes your predicate more legible.
Lets say we have a set like this
NSSet *mySet = [NSSet setWithObjects:#"Box", #"Ball", #"Bat", nil];
and I have search string Boxer
NSString* searchString = #"Boxer";
now once i search for Boxer in NSSet it should return a true, since a substring 'Box' exists which is prefix of "Boxer". Is this possible?
if the search string is "kickboxer" or "basketball" should return false.
If is so whats the best case. I know iterating and comparing would be possible with O(n) time is it possible to better it?
you need to use predicate llike this:
NSMutableArray *cars = [NSMutableArray arrayWithObjects:#"Maruthi",#"Hyundai", #"Ford", #"Benz", #"BMW",#"Toyota",nil];
NSString *stringToSearch = #"i";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",stringToSearch]; // if you need case sensitive search avoid '[c]' in the predicate
NSArray *results = [cars filteredArrayUsingPredicate:predicate];
You need to define the problem better. What substrings do you want to search for? Only substrings starting from the beginning? Is there a minimum length? So if your search string is "Boxer" you'd match "Box" but not "over"?
Or do you want to only search for the "stems" of words? If so then you'll need a dictionary API that lets you look up the stems of words.
If ANY substring will match then you have a much harder job. You'd have a LOT of different strings to match for. (B/o/x/e/r/Bo/Box/Boxe/Boxer/ox/oxe/oxer/xe/xer/er. And it gets worse for longer strings.)
#Arcky's predicate solution will let you search for substrings. Now you just need to identify all possible substrings that you want to match on, and issue a predicate for each one.
What you want is to retun true if a Substring of the search string is on the Set?
The right way should be use the NSPredicate as #Arcky say before. But that matches if the searchString is a substring of one of your set elements not the oposite.
For that you couldn't use predicates. You can allways do that by brute force:
NSSet *mySet = [NSSet setWithObjects:#"Box", #"Ball", #"Bat", nil];
NSString* searchString = #"Boxer";
foreach(id obj in mySet){
if ([searchString containsString:obj]) { //NOTE: Only works in iOS 8.0+
return true;
}
}
I have a NSFetchResult that returns managed objects that contain MANY related objects ( Aobj ->> Bobj). The "Bobj" managed object contains a BOOL attribute "isSet" (stored as NSNumber). The NSFetchResult returns all Aobj objects.
I would like a suggestion for an NSPredicate that would return an Aobj with only those Bojs where isSet is TRUE (#1).
Currently, I enumerate over my NSArray of Aobjs returned by my fetch result so that I can get my filtered NSArray of Aobjs this way:
- (NSArray *)filteredObject:(Aobj *)aObj
{
NSMutableArray* bObjs = [aObj.bObjs mutableCopy];
[bObjs enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Bobj* bObj, NSUInteger idx, BOOL *stop)
{
if (bObj.isSet == [NSNumber numberWithBool:NO])
{
[bObjs removeObject:story];
}
}];
return bObjs;
}
Asked another way: how would I define an NSPredicate for my NSFetchResult that would get all my Aobjs but limit the related Bobjs to those matching Bobj.isSet == YES?
Thanks in advance!
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"SELF.bObjs.isSet == YES"];
[myFetchRequest setPredicate:myPredicate];
NSArray *array = [moc executeFetchRequest:myFetchRequest error:&error];
Use a subquery. Subqueries follow this general format:
SUBQUERY(relationship, related_thing, predicate)
relationship is the relationship on the object being evaluated.
related_thing is an individual object in the relationship. It's prefixed by a $ and is then used in the predicate (not shown above, for clarity).
predicate is the predicate to apply to related_thing.
With your example, it would look like (assuming your relationship is called 'bobjs'):
SUBQUERY(bobjs, $obj, $obj.isSet == YES)
Subqueries can be quite powerful. For example, you can also apply collection operators to the subquery:
SUBQUERY(bobjs, $obj, $obj.isSet == YES).#count > 1 would give you each AObj that has at least one object in the 'bobjs' relationship with isSet equal to YES. Obviously, you can use other collections operators or layer logical operations or additional expressions on top of this.
I'm having some trouble using NSPredicate predicateWithFormat:argumentArray:. In this example, serverIDList is array of strings. Results is an array of NSManagedObjects with an attribute named "flid" which is a string.
NSMutableString *predicateString = [[NSMutableString alloc] init];
[predicateString appendString:#"(flid IN %#)"];
[results filterUsingPredicate:[NSPredicate predicateWithFormat:predicateString argumentArray:serverIDList]];
The problem is that [NSPredicate predicateWithFormat:predicateString argumentArray:serverIDList] evaluates to "flid IN '2155'", which is only the first value of the array serverIDList. I can't seem to get the predicate to evaluate the entire array. Is there something missing here?
Thanks!
[NSPredicate predicateWithFormat:#"(flid IN %#)" argumentArray:serverIDList]
is equivalent to
[NSPredicate predicateWithFormat:#"(flid IN %#)", id1, id2, ..., idN]
where id1, ..., idN are the elements of the array serverIDList.
That should explain why only the first element is evaluated.
What you probably want is
[NSPredicate predicateWithFormat:#"(flid IN %#)", serverIDList]
Remark: I would recomment not to create predicates as strings first. The chances for
quoting or escaping errors are quite high. Use only predicateWithFormat with a
constant format string. If you have to combine predicates dynamically at runtime,
use NSCompoundPredicate.
I'm trying to do a simple predicate filter on an array of objects.
The objects in the array have 2 properties, displayValue and value. I am trying to filter based on a search string and I get a crash.
NSPredicate *pred = [NSPredicate predicateWithFormat:#"displayValue CONTAINS[cd] %#", searchString];
NSArray *results = [_data filteredArrayUsingPredicate:pred];
what exactly is incorrect about this format that it causes a Can't use in/contains operator with collection 100 (not a collection) crash?
I was able to reproduce your problem. This happens if the displayValue of one of the objects
is not a NSString, but some different type.
From your error message, I assume that you assigned an NSNumber, for example
obj.displayValue = #100;
somewhere in your code. The "CONTAINS" predicate works only with strings, so you must assign
only string values to the property.
Therefore, you should define the type of the property as
NSString * instead of id, and check the assignments to that property.
If you really need to keep the id type and store different kinds of objects in that property,
then you cannot use the "CONTAINS" operator in the predicate. A direct comparison
with #"displayValue == %#" would work, however.
UPDATE: As a workaround, you could use the description method, which converts any object
to a string, in particular it converts a NSNumber to its string representation. So the following could work:
[NSPredicate predicateWithFormat:#"displayValue.description CONTAINS[cd] %#", searchString];
The drawback is that the exact description format is not documented.
Another solution could be to use a block-based predicate, where you can check the type
of each object and perform the appropriate comparison.