Searching Arrays with Realm (iOS) - ios

Hiho,
I have a question about querying a realm database. I have realm-objects which contains features in a RLMArray. I want to find all objects which contains all features from a given array.
And in another case I want to find all object which have one of the features.
I try things like this (also with IN):
NSArray *featureArray = #[#"feature1", #"feature2"];
RLMResults* containedObjects = [MyObject objectsWhere:#"features CONTAINS %#", featureArray];
But get errors like: RLMArray predicates must use aggregate operations
I think the problem is, that realm have to check an array with an array for matching. In SQL I think a JOIN-Operation would be a good (not very efficent) way, but I can't found an equivalent operation in NSPredicate.
Thank you for every hint!

As you can't store arrays of values in Realm, I guess that MyObject would have in that case a to-many relation / list property to a class Feature, which might be identified by their name.
CONTAINS is with NSPredicate not what you're looking for here. This is a string comparison operator.
You can use the IN operator, as seen below:
NSArray *featureArray = #[#"feature1", #"feature2"];
RLMResults* containedObjects = [MyObject objectsWhere:#"features.name IN %#", featureArray];
Note: you don't have to worry about performance, as links are first-class citizen in Realm, there are no expensive JOIN operations involved here.

Related

NSFetchRequest based on array property size

I have a NSManagedObject which has a (transformable) NSArray property arrayProperty.
I would like to execute a fetch request filtering for objects whose property size is less than 5. I tried
[NSPredicate predicateWithFormat:#"arrayProperty.#count <= 5"]
and
[NSPredicate predicateWithFormat:#"arrayProperty[SIZE] <= 5"]
but none of them work (giving Unsupported function expression count and Unsupported function [SIZE]).
Is there a way to achieve this?
This kind of thing isn't possible with transformable properties. Transformable are convenient for storing data, but once they're stored, that's all they are-- a bunch of bytes with no information about the data structure that created them. SQLite doesn't know that it's an array, so it's not able to do things like count how many items are in the array.
If you use transformable properties, you lose any ability to filter based on them, except for extremely basic tests like whether the value is nil.
If you need to do this kind of filtering, you'll need to consider how to change your data model. One possibility would be that instead of saving an array, create a new entity with a to-many relationship to store the values.

SharkORM - How to parse results of Joins

I am trying to use SharkORM to fetch relationships across my objects. I am familiar with the joinTo syntax defined in SharkORM's documentation, but I am not sure how to use the resulting joinedResults object to get the related objects I need.
[[Person query] joinTo:[Department class] leftParameter:#"department" targetParameter:#"Id"]
outputs
{
"Department.Id" = 61;
"Department.location" = 35;
"Department.name" = Development;
}
into the Person.joinedResults field, but how do I take those results and get a Department object back. I've tried making a call to person.department after the joinTo, but it seems to make a second query to the database as if I hadn't used joinTo at all.
Am I really expected to parse the dictionary results of person.joinedResults into a Department object manually? That is very cumbersome, especially when you start joining more than one relationship.
I feel as if I am missing some obvious way to use the results of the joinTo.
The join functionality is in addition to relationships. So you can reference unrelated (or related) tables in a query, but then objects you get back can only ever be physically structured as per your original class, and are defined from the data and not from your query.
So, in a nut shell; joinTo: will enable you to reference remote or unrelated objects in your query for selection.
But to traverse object relationships you would use the properties.
Person* p = GetFirstPerson()
// reference/traverse the object to look at relationships (1:1)
p.department
or
p.department.location
So I guess what i'm saying is, even though it is SQL like syntax, you can only ever end up with rigidly defined classes in a fixed structure as a result unless using other interfaces such as sum, distinct etc..

Efficient way to get relationship count in database

I want to know what is the best way to get count of related entities in to-many relationship. Let's say I have a data model that looks like this (simplified), and I want to know the number of passengers for each bus:
Currently I can think of two options:
Add an extra attribute to bus entity called passengerCount which will be updated every time a passenger is added/removed.
Every time the count of passengers needs to be displayed, it's done by fetching the passengers and displaying their count.
Both of my options seem quite inefficient, even though I'm not aware how heavy it is to update/fetch values with core data. For example, imagine doing number 2 for every table view cell.
My question is: What is the best way to do this? A method in NSManagedObject class perhaps (I couldn't find any) or some other way that is more efficient?
Three remarks at the very beginning:
A. You should care about efficiency when you have a runtime problem. "Premature optimization is the root of all evil." (Donald Knuth)
B. Who said that all passenger entities has to be fetched? You think of something like this …
[bus.passengers count]
… causing passengers to be fetched. But Core Data supports faulting, so maybe the entities are maybe fetched into fault. (Having only an id, but not the full object.)
C. You can see what Core Data does, when you turn verbose mode on. To do so pass the launch argument
-com.apple.CoreData.SQLDebug 1
To your question itself:
If you really have a problem, you can ask for a count explicitly with -countForFetchRequest:error:.
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"passenger"];
fetch.predicate = [NSPredicate predicateWithFormat:#"bus == %#", bus];
…
NSUInteger count = [context countForFetchRequest:fetch error:NULL]; // Please pass an NSError instance in real world
Typed in Safari.
The XCode auto-generated NSManagedObject class for your Core Data entity bus contains a property for its to-many relationships to Passenger objects.
You can think of this property like a "computed attribute" of your entity (meaning you will not set the attribute yourself but Core Data updates it automatically when you add or delete a relationship). This property is an NSSet? (with references to the related Passenger objects) and the NSSet supports the .count method.
So you can use .count without a special fetch request.

Get the opposite of intersection between array A and array B

quick question. I have two NSMutableArray:
Array 1: [A,B,C,D,E,F];
Array 2: [B,E,F];
Note that Array 2 is always subset of Array 1 - meaning objects that exist is Array 2, definitely exist is Array 1 as well.
So what I want is to build an array that contain the objects that are NOT in Array 2. Like so
Array 3: [A,C,D];
I've tried using relative complement as outlined in this post but the resulting array is basically the same as Array 1. It doesn't eliminate the objects that exist in Array 2.
I also tried the answer here as well, but still not getting what I want. Unless i'm really doing something very obviously wrong.
Using NSPredicate is much preferable, I guess. But I'm open to ideas and hints.
Note: Just for context, i'm doing this to update my UITableView, basically for data filtering purposes.
Thanks!
UPDATE
So all the answers given so far actually works with simple set of dummy data for me. But when I tested with my real data, the Array 3 that are created is still the same as the Array 1. So, I'm going to give more info about my stuff.
Both arrays are NSMutablArray that store dictionary objects. I'm actually using Parse.com, so the objects in both arrays are PFObject (which is just NSObject, if I'm not mistaken). I don't know how does this affect anything, but yeah, seems to not be working.
Here is a screenshot from the console when I try to step through the process.
Thanks for the help so far guys.
There's no need to go down the predicate route here, you know explicitly what you want to do, and can be expressed with simple, native APIs.
NSMutableArray *mArray3 = [NSMutableArray arrayWithArray:array1];
[mArray3 removeObjectsInArray:array2];
NSArray* array3 = [mArray3 copy];
An important thing to note:
removeObjectsInArray:
This method assumes that all elements in otherArray respond to hash and isEqual:.
For an object to be deemed equal, they need to response to hash and isEqual:, and for those values to match between two equal objects. A good article regarding equality can be read here.
If PFObject simply inherits from NSObject, then the equality checking will be very basic. It will simply check for equality by asking "Are these objects the same object, based on location in memory?". This probably explains why your dummy data works, but the real data does not.
You'll need to subclass PFObject to make it aware of the contents. This means you can override hash and isEqual: to provide a more reasonable statement of equality. For example, "Are these objects the same object, based on the value of the 'name' property". It's up to you to define what makes objects equal.
WDUK's answer is probably the way to go since it's simpler and requires only one new object (plus a copy of that). However, if you like discrete math, NSMutableSet allows you to perform set operations. That is, another (overly complicated, however, very descriptive) answer to your question is:
// convert arrays to sets.
// since array2 is always a subset of array1, we don't need to create a union set.
NSMutableSet *set1 = [NSMutableSet setWithArray:array1];
NSSet *set2 = [NSSet setWithArray:array2];
// find intersecting objects
NSMutableSet *intersection = [NSMutableSet setWithSet:set1];
[intersection intersectSet:set2];
// remove intersecting objects (result: your desired set)
[set1 minusSet:intersection];
NSArray *nonIntersectingObjects = [set1 allObjects];
As WDUK suggests, your problem is easily solved with an NSMutableArray. However, when similar, but more complex, problems arise, set operations might provide an simpler and more elegant solution.
If you want to do it using a predicate here's the way to do it:
NSArray* array1= #[#'A',#'B',#'C',#'D',#'E',#'F'];
NSArray* array2= #[#'B',#'E',#'F'];
NSPredicate* predicate= [NSPredicate predicateWithFormat: #"not(self in %#)",array2];
NSArray* array3=[array1 filteredArrayUsingPredicate: predicate];
SELF represents the evaluated object in the array. The IN operator can be used to check if any object is inside a collection, here is some reference: Predicate programming guide / aggregate operations

Remove duplicates from a NSMutableArray with objects added in a 'for' loop

Now, I've looked up on this on here and google, and it seems everyone uses NSSet to remove dupes. This is cool and all, but it seems that this method removes the sorting as well.
Is there
1) A way to sort NSSet alphabetically?
2) A better way to remove dupes in NSMutableArray in or outside the for loop where I add them to the array by reading them from a .csv file.
Thanks:)
I believe you want to be using an NSOrderedSet. Here's the documentation on it:
http://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSOrderedSet_Class/Reference/Reference.html
You can construct an NSOrderedSet* from your array; using +orderedSetWithArray: will preserve the array's existing order. If it's not already in the correct order, you can't sort the set directly, so there's little point in using an ordered set, but you can easily construct a regular NSSet and sort that into another array, using -sortedArrayUsingDescriptor:, or even better allObjects, followed by any of NSArray's sorting methods.
On the other hand (it's possible I'll get some nasty comments about this, but...), since NSArray and NSSet seem to be built on top of the same hash table functionality, you could just do this:
id newObj = // Acquire new object from wherever
while( newObj ){
if( ![arrayImConstructing containsObject:newObj] ){
[arrayImConstructing addObject:newObj]
}
newObj = // Acquire next object
}
This is basically what an ordered set has to do when you construct it anyways, and it's quite likely that your array is small enough (if you're putting it into a UIPicker) that you won't notice a performance difference at all. Still, measure the two, and then decide.
*NSOrderedSet is available on iOS > 5.0 (or OS X > 10.7).
NSOrderedSet gives you ordering in a set.

Resources