NSPredicate to many like a <----->> b----->c - objective-c

I have entities: Language, Proper and Answer.
model look like Language{A:name(NSString), R:propers(NSSet)} --->> Proper{A:name(NSString), R:answer(Answer)} ---> Answer{A:answer(NSString)}
So, i got NSDictionary with params: {#"key1", #"value1"}, {#"key2", #"value2"}... i
I need create NSPredicate from this dictionary to get all Languages where propers.name = key[i] and propers.answer.answer = value[i] from my NSDictionary.
Example:
C++
level : high
try/catch : yes
typization : static
Java
level : high
try/catch : yes
typization : dynamic
NSDictionary : {level : hight}, {try/catch : yes}, {typization : dynamic}
//make and set NSPredicate to array controller
//array controller arrangedObjects will return Java
Sorry for bad grammar :/
Update
*After 2 weeks of sleepless nights and work on expert system teacher took a laboratory without checking it. Kill me please. Thanks a lot to all of you.*

I am just guessing what you want to do so here is a code:
- (NSPredicate *)constructPredicateWithDictionary:(NSDictionary *)dictionary
{
NSArray *allKeys = [dictionary allKeys];
NSMutableArray *predicates = [NSMutableArray array];
for (NSString *key in allKeys) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(B, $B, $B.key = %# && $B.C.value = %#).#count > 0", key, [dictionary valueForKey:key]];
[predicates addObject:predicate];
}
//not quite sure what you need so I am guessing
NSPredicate *finalAndPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates]; //if you want all the predicates to be concatenated with and '&&' - logical expression - so all of the subqueries have to be correct
NSPredicate *finalOrPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates]; //if you want all the predicates to be concatenated with or '||' - logical expression - so any of the subqueries has to be correct
return finalOrPredicate; //return the needed predicate
}

You need from SUBQUERY. Something like:
[NSPredicate predicateWithFormat:#"B.key = %# AND SUBQUERY(B, $B, $B.C.value = %#).#count > 0", key, value];
The SUBQUERY iterates through the objects and you can also have nested SUBQUERYs if you have more than one to-many relationships.
You can use and "ANY ..." but it doesn't work in all cases.

Related

How do I create an NSPredicate for a subquery given an existing NSPredicate?

In my app, one Audit has many Findings.
I have a method which returns an NSPredicate which filters findings by search text and type ID:
+ (NSPredicate)predicateWithSearchText:(NSString *)searchText typeId:(NSNumber *)typeId;
I want to use this predicate to filter an array of Audits according to the properties of their findings. So, given the output of the first method, I want to create a new predicate equivalent to:
[NSPredicate predicateWithFormat:#"SUBQUERY(findings, $x, ????).#count > 0"]
...where ???? applies my existing predicate to the audit's findings.
How can I do this without exposing the logic inside +predicateWithSearchText:typeId:?
Is something like this what you're looking for?
- (void)sampleMethod:(NSPredicate *)predicateWithSearchText {
NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
BOOL auditFinding = [self auditFinding:object];
BOOL predFinding = [predicateWithSearchText evaluateWithObject:object];
return auditFinding && predFinding;
}];
}

CoreData - Getting a NSManagedObject's relationship subset

Noob question incoming (very simple but not finding the answer anywhere):
I have an entity
MY_ENT *my_ent = {initialized elsewhere};
This entity has a to-many relationship called my_rel
NSLog(#"Relations: %lu", my_ent.my_rel.count);
Relations: 15
I'd like to get a subset of it, where the field my_field equals to #"xx"
I tried to loop on the relations populating a NSArray but no luck (pointers seem deallocated).
NSMutableArray *my_rels;
for (MY_REL *my_rel in my_ent.my_rel) {
if ([my_rel.my_field isEqualToString:#"xx"]) {
[my_rels addObject:my_rel];
}
}
Maybe I should use a predicate but I don't understand how to use one here.
Yes, it's easier with a predicate. You can do this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"my_field = %#", x];
NSSet *subset = [my_ent.my_rels filteredSetUsingPredicate:predicate];

NSPredicate with 64-bit integers?

I'm trying to use NSPredicate with 64-bit numbers, but for some reason it's failing. I create a predicate with the following string:
[NSString stringWithFormat:#"UID == %qu", theUID]
And it always fails. Yet if I loop through my objects and compare if ( UID == theUID ) I find one that is fine. I'm really confused as to why this isn't working correctly.
Does the above look correct? My predicates work fine for other integers or even strings. But this seems to be failing.
Thanks!
Edit: So strange... so I create my predicate by doing:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:myString];
Now when I print myString, and then print myPredicate (by doing NSLog(#"%#", blah);) I get:
String: UID == 17667815990388404861 Predicate: UID ==
9223372036854775807
It's the same string, why are these different?
Possibly theUID is not an unsigned value?
A more robust way to build predicates would be to pass in an NSNumber:
NSNumber *theUIDNum = [NSNumber numberWithUnsignedLongLong:theUID];
[NSString stringWithFormat:#"UID == %#", theUID]
This gives you the bonus ability to debug and print out the value of theUINum to make sure it got the transition right.
In your NSPredicate you can use something like this
let myNSNumber = NSNumber(value: theUID)
let predicate = NSPredicate(format: "UID = %i", myNSNumber.int64Value)

Writing an NSPredicate that returns true if condition is not met

I currently have the following piece of code
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF contains '-'"];
[resultsArray filterUsingPredicate:pred];
This returns an array with the elements which contain '-'. I want to do the inverse of this so all elements not containing '-' are returned.
Is this possible?
I've tried using the NOT keyword in various locations but to no avail. (I didn't think it would work anyway, based on the Apple documentation).
To further this, is it possible to provide the predicate with an array of chars that I don't want to be in the elements of the array? (The array is a load of strings).
I'm not an Objective-C expert, but the documentation seems to suggest this is possible. Have you tried:
predicateWithFormat:"not SELF contains '-'"
You can build a custom predicate to negate the predicate that you already have. In effect, you're taking an existing predicate and wrapping it in another predicate that works like the NOT operator:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF contains '-'"];
NSPredicate *notPred = [NSCompoundPredicate notPredicateWithSubpredicate:pred];
[resultsArray filterUsingPredicate:pred];
The NSCompoundPredicate class supports AND, OR, and NOT predicate types, so you could go through and build a large compound predicate with all the characters you don't want in your array, then filter on it. Try something like:
// Set up the arrays of bad characters and strings to be filtered
NSArray *badChars = [NSArray arrayWithObjects:#"-", #"*", #"&", nil];
NSMutableArray *strings = [[[NSArray arrayWithObjects:#"test-string", #"teststring",
#"test*string", nil] mutableCopy] autorelease];
// Build an array of predicates to filter with, then combine into one AND predicate
NSMutableArray *predArray = [[[NSMutableArray alloc]
initWithCapacity:[badChars count]] autorelease];
for(NSString *badCharString in badChars) {
NSPredicate *charPred = [NSPredicate
predicateWithFormat:#"SELF contains '%#'", badCharString];
NSPredicate *notPred = [NSCompoundPredicate notPredicateWithSubpredicate:pred];
[predArray addObject:notPred];
}
NSPredicate *pred = [NSCompoundPredicate andPredicateWithSubpredicates:predArray];
// Do the filter
[strings filterUsingPredicate:pred];
I make no guarantees as to its efficiency, though, and it's probably a good idea to put the characters which are likely to eliminate the most strings from the final array first so that the filter can short-circuit as many comparisons as possible.
I'd recommend NSNotPredicateType as described in the Apple documentation.

filtering NSArray into a new NSArray in Objective-C

I have an NSArray and I'd like to create a new NSArray with objects from the original array that meet certain criteria. The criteria is decided by a function that returns a BOOL.
I can create an NSMutableArray, iterate through the source array and copy over the objects that the filter function accepts and then create an immutable version of it.
Is there a better way?
NSArray and NSMutableArray provide methods to filter array contents. NSArray provides filteredArrayUsingPredicate: which returns a new array containing objects in the receiver that match the specified predicate. NSMutableArray adds filterUsingPredicate: which evaluates the receiver’s content against the specified predicate and leaves only objects that match. These methods are illustrated in the following example.
NSMutableArray *array =
[NSMutableArray arrayWithObjects:#"Bill", #"Ben", #"Chris", #"Melissa", nil];
NSPredicate *bPredicate =
[NSPredicate predicateWithFormat:#"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
[array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { #"Bill", #"Ben" }.
NSPredicate *sPredicate =
[NSPredicate predicateWithFormat:#"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { #"Chris", #"Melissa" }
There are loads of ways to do this, but by far the neatest is surely using [NSPredicate predicateWithBlock:]:
NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
return [object shouldIKeepYou]; // Return YES for each object you want in filteredArray.
}]];
I think that's about as concise as it gets.
Swift:
For those working with NSArrays in Swift, you may prefer this even more concise version:
let filteredArray = array.filter { $0.shouldIKeepYou() }
filter is just a method on Array (NSArray is implicitly bridged to Swift’s Array). It takes one argument: a closure that takes one object in the array and returns a Bool. In your closure, just return true for any objects you want in the filtered array.
Based on an answer by Clay Bridges, here is an example of filtering using blocks (change yourArray to your array variable name and testFunc to the name of your testing function):
yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [self testFunc:obj];
}]];
If you are OS X 10.6/iOS 4.0 or later, you're probably better off with blocks than NSPredicate. See -[NSArray indexesOfObjectsPassingTest:] or write your own category to add a handy -select: or -filter: method (example).
Want somebody else to write that category, test it, etc.? Check out BlocksKit (array docs). And there are many more examples to be found by, say, searching for e.g. "nsarray block category select".
Assuming that your objects are all of a similar type you could add a method as a category of their base class that calls the function you're using for your criteria. Then create an NSPredicate object that refers to that method.
In some category define your method that uses your function
#implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
return someComparisonFunction(self, whatever);
}
#end
Then wherever you'll be filtering:
- (NSArray *)myFilteredObjects {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"myMethod = TRUE"];
return [myArray filteredArrayUsingPredicate:pred];
}
Of course, if your function only compares against properties reachable from within your class it may just be easier to convert the function's conditions to a predicate string.
NSPredicate is nextstep's way of constructing condition to filter a collection (NSArray, NSSet, NSDictionary).
For example consider two arrays arr and filteredarr:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",#"c"];
filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
the filteredarr will surely have the items that contains the character c alone.
to make it easy to remember those who little sql background it is
*--select * from tbl where column1 like '%a%'--*
1)select * from tbl --> collection
2)column1 like '%a%' --> NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",#"c"];
3)select * from tbl where column1 like '%a%' -->
[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
I hope this helps
Checkout this library
https://github.com/BadChoice/Collection
It comes with lots of easy array functions to never write a loop again
So you can just do:
NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
return object.age.intValue < 20;
}];
or
NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
return object.age.intValue < 20;
}];
The Best and easy Way is to create this method And Pass Array And Value:
- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
NSMutableArray *temArr=[[NSMutableArray alloc] init];
for(NSDictionary *dic in self)
if([dic[key] isEqual:value])
[temArr addObject:dic];
return temArr;
}
Another category method you could use:
- (NSArray *) filteredArrayUsingBlock:(BOOL (^)(id obj))block {
NSIndexSet *const filteredIndexes = [self indexesOfObjectsPassingTest:^BOOL (id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
return block(obj);
}];
return [self objectsAtIndexes:filteredIndexes];
}