Core Data Fetch Results Reduced Set of Related Items - ios

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.

Related

Core Data, Get Sum Of Certain Boolean Value Attribute

I'm logging a Core Data attribute "passed" (Boolean value)
for (Circuit *object in self.distributionBoard.circuits) {
NSLog(#"Core Data Value = %d", object.passed);
}
This logs fine. What's the most efficient way to count the number of times the saved boolean value == 1?
Using NSFetchReques or NSExpression did not yield the desired result so far. Looked here: Core Data sum of all instances attribute and similar, with the usual searches
Since your property is a boolean, you can make it a lot simpler than the methods described in that answer. Use a predicate to match the value of passed and then get the count of the result instead of the fetched objects. Something like:
NSFetchRequest<Event *> *fetchRequest = MyEntity.fetchRequest;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"passed = true"];
NSError *error = nil;
NSUInteger count = [self.managedObjectContext countForFetchRequest:fetchRequest error:&error];
Then count has the number of instances where passed is true.

Fetch Realm objects that contain parameter

I have a realm database filled with Restaurant objects that have a url parameter. I'd like to fetch the objects where the url contains what the user has typed in a UITextField.
NSPredicate *pred = [NSPredicate predicateWithFormat:#"url CONTAINS '%#'", self.searchQuery];
RLMResults<Database *> *results = [Database objectsWithPredicate:pred];
if ([results count] == 0) {
NSLog(#"results count == 0");
return nil;
}
Now, when I run this, I always get 0 results. Eventhough I have two example entries that have 'http://www.testingthis.com' as an url and the search query is 'testingthis'. Any ideas as to why I'm not getting results?
You need to perform your search query against the Restaurant class itself; not Database. Realm uses the class you specify to determine which table in which to apply the query.
As an aside, you can also inline NSPredicate queries with +[RLMObject objectsWhere:], so it's not necessary to create a separate NSPredicate object (unless you've got a specific reason). :)
RLMResults *restaurants = [Restaurant objectsWhere:#"url CONTAINS '%#'", #"testingthis"];
If you're trying to query for Restaurant objects that aren't in the default Realm, then you can alternatively use +[RLMObject objectsInRealm: where:] to explicitly control which database you're querying.

How to sort NSSet subset in same order as full set NSArray, using NSPredicate?

My self.allArray contains all my objects. Then self.enabledSet contains a subset of these objects.
To create a sortedEnabledArray I currently do this:
NSArray* enabledArray = [self.enabledSet allObjects];
NSArray* sortedEnabledArray;
sortedEnabledArray = [enabledArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
int indexA = [self.allArray indexOfObject:a];
int indexB = [self.allArray indexOfObject:b];
if (indexA < indexB)
{
return NSOrderedAscending;
}
else if (indexA > indexB)
{
return NSOrderedDescending;
}
else
{
return NSOrderedSame;
}
}];
but I am wondering if this can't be done in a smarter/shorter way, using an NSPredicate for example. Any ideas?
EDIT:
One idea to shorten is:
int difference = indexA - indexB;
// Convert difference to NSOrderedAscending (-1), NSOrderedSame (0), or NSOrderedDescending (1).
return (difference != 0) ? (difference / abs(difference)) : 0;
It depends on number of items in your set and array, but you can do this way:
NSPredicate* predicate = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary* bindings)
{
return [self.enabledSet containsObject:object];
}];
NSArray* sortedEnabledArray = [self.allArray filteredArrayUsingPredicate:predicate];
My suggestion is to use a different approach. There is a companion to NSArray called NSIndexSet (and it's mutable counterpart, NSMutableIndexSet). It is an object that is specifically intended to keep track of subsets of an array that meet a given criteria.
NSArray includes methods like indexesOfObjectsPassingTest (and other variants that include additional parameters.) that let you add the indexes of some members of an array to an index set.
Once you have an index set that represents a subset of your allArray, you could use a method like objectsAtIndexes to get an array of just the selected objects.
Store in NSSet not objects, but indexes from allArray array.

NSPredicate filter to-Many with child object property

I have what I thought was a simple problem. I am attempting to filter some core data where I have a Parent object which has a to-many relationship with a child object and that child object has a string id. I want to get all the parent objects where no child object has a specific id.
I have tried !(ANY... LIKE) as well as !(ANY..==) and NONE with like and == and ALL children.id != otherid
My querying looks like:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Parent"];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"NONE children.id LIKE %#",otherID];
[fetchRequest setPredicate: predicate];
NSError* error;
NSArray* allParents = [[DataManager context] executeFetchRequest:fetchRequest error:&error];
//sanity check the predicate
for (Parent* p in allParents) {
for (Child* c in p.children) {
if([c.id isEqualToString:otherID]){
NSLog(#"PREDICATE FAIL!");
}
}
}
Am I missing something with NSPredicate? Is this type of filtering allowed for CoreData? Better solution?
I found a similar question although not easily apparent. It turns out the answer is the tricky SUBQUERY. Here is what led me on the chase:
NSPredicate Aggregate Operations with NONE
and an more open explanation about SUBQUERY here:
http://funwithobjc.tumblr.com/post/2726166818/what-the-heck-is-subquery
The resulting predicate is:
//in children get a $child and group all the $child objects together
//where the ids match, if that groups count is 0 we know
//the parent has no child with that id
[NSPredicate predicateWithFormat:
#"SUBQUERY(children, $child, $child.id == %#).#count == 0",objectId];

NSPredicate for two NSNumber arrays

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.

Resources