Find NSManagedObjects from an NSArray that share a property - ios

I have a fairly large number of NSManagedObjects in an NSArray and need to check whether do any of them have the same value for a property. The obvious way is nested for loops however it will take ages to go through all of them as there are about a 1000 objects in the array.
for (NSManagedObject *object in array) {
for (NSManagedObject *secondObject in array {
if ([[object valueForKey:#"key"] isEqualTo:[secondObject valueForKey:#"key"]] &&
object != secondObject) {
NSLog(#"Sharing a property");
}
}
}
Any better way to do this? If there are 1000 objects that accounts to 1 000 000 comparisons, that might take some time.

You could use an NSDictionary. Each entry would be made from the following pair:
key would be equal to the selected NSManagedObjects attribute
value would be an NSArray of NSManagedObjects, that share this attribute's value

Get the list of key values for the objects in the array, then turn that into a set. If the size of the set is the same as that of the original array, there are no matches.
If you need to know which objects match, use a dictionary to create a multiset -- each key has an array of the objects as its value.
Creating your own keyed set class is also an option.

You can sort the array according to the values of that property.
Then a single loop over
the array is sufficient to find objects sharing the same value of the property.

Related

NSMutableDictionary add object by sequence issue

I added sequence in NSMutableDictionary by keys
Here is the key of Dictionary
New,
To Do,
Basic,
Advanced,
In Progress,
Done
But when I print NSMutableDictionary all keys then output was different
Advanced,
In Progress,
To Do,
New,
Done,
Basic
Any solution for this ?
Dictionaries are unordered collections. The sequence of items is no guaranteed, and will not be preserved. Don't make any assumptions about the order objects are fetched from a dictionary. It is not specified and subject to change.
If you need an ordered collection, try using an array of single-element dictionaries, or an array of custom objects with a key and value property.
You can try the following or come up with own data structure if you are worried about the position of elements inside a Dictionary
You can use Array of key-value pairs like this -> [[String : AnyObject]].
Which looks like : [["New" : 1],["In Progress" : 3],...]
You can also use tuples of key and value in an Array like this -> [(key,value)]
Which looks like : [("New" , 1),("In Progress" , 3),...]
Try to get keys in this array
NSArray *orderedArr = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedStandardCompare:)];

How to select objects from an NSArray that are contained in another NSArray using a predicate

I have an NSArray of objects each of with contain a "key" field
I have another NSArray with nested NSArrays grouping those objects by a different field "group by field" for instance a display name (alphabetic grouping)
For instance
#interface MyObject
#property (nonatomic) int key;
#property (nonatomic,strong) NSString *groupByKey;
#end
NSArray *aBounchOfObjectsInAFlatList = #[obj1,obj2,obj3....];
NSArray *groupByArrayOfObjects = #[#[obj1_tag,obj2_tag],#[objn_tag,objk_tag]....];
Important to note that obj1 != obj1_tag - they only share values of "groupByKey" and/or "key".
I would like to find the index of each obj_tag where (obj_tag.key == obj.key) from the flat array.
Now I am running 2 loops and saving the counter values - creating index paths for the matches.
I assume there is a clear way using NSPredicates to create an array of NSIndexPaths for the corresponding objects but I am not clear how to do this
NSPredicate is just a predicate and nothing more. It is applied to some object and
yields YES or NO. A predicate can be used for filtering an array, but since the predicate itself is applied to each single array element, it does not "know" the location
(or index) of the object being tested.
Therefore a NSPredicate cannot create an array of NSIndexPaths.
The NSArray method indexOfObjectPassingTest: can be useful to find the location
of an object in an array, but even that method has to loop over all array elements
to find the object. So it might help to make the code shorter or better readable, but
it does not change the performance.
If groupByArrayOfObjects is really large, then you can create a mapping (an NSDictionary) from each key to the corresponding index path first. This requires only one enumeration of the (nested) array. Then you can use this mapping to get
the index path for each element in the other array aBounchOfObjectsInAFlatList.

Performing valueForKeyPaths against multiple keyPaths

Let's say I have an object with some number of properties and I load up 1000s of these objects into an array. Next, I perform a series of valueForKeyPaths against these properties:
result.property1 = [array valueForKeyPath:#"#sum.property1"];
result.property2 = [array valueForKeyPath:#"#sum.property2"];
result.property3 = [array valueForKeyPath:#"#sum.property3"];
etc...
Summing these properties individually seems pretty inefficient. Is there a better way besides fast enumerating over the properties and summing them manually? i.e.
for(Foo* foo in array) {
result.property1 += foo.property1;
result.property2 += foo.property2;
result.property3 += foo.property3;
}
KVC requires keys to be strings:
A key is a string that identifies a specific property of an object. Typically, a key corresponds to the name of an accessor method or instance variable in the receiving object. Keys must use ASCII encoding, begin with a lowercase letter, and may not contain whitespace.
So the answer as far as I know is unfortunately you can't do this with valueForKeyPath: you would have to manually do it or enumerate over it.

Sort multiple arrays

Here is my situation:
I manipulate 6 NSMutableArrays. One of them has NSDates objects in it, the other ones have NSNumbers. When I populate them, I use addObject: for each of them, so index 0 of each array contains all the values I want for my date at index 0 in the dates array.
I want to make sure that the arrays are all sorted according to the dates array (order by date, ascending), meaning that during the sorting, if row 5 of the dates array is moved to row 1, it has to be applied to all the other arrays as well.
I was previously using CoreData, but I must not use it anymore (please don't ask why, this is off-topic ;) ). In CoreData, I could use an NSSortDescriptor, but I have no idea on how to do it with multiple NSArrays...
As always, hints/answers/solutions are always appreciated :)
This is a common problem. Use the following approach.
Encapsulate your six arrays into an object - every instance will have six properties.
Implement compare: method on this object, using [NSDate compare:] (this step can be skipped but it's cleaner this way).
Now you have only one array - sort it using the method from step 2.
I think the better solution for you to have NSArray of NSDictionary objects.
NSArray *allValues = [[NSArray alloc] init];
NSDictionary *dict = #{"Date" : [NSDate date], #"Key1" : #"Value1", #"Key2" : #"Value2"};
Then you can sort this array with sortDescriptor without any problems.
And then you can also use Comparator or Sort Desriptor as you wish.
Wrap all your items that you are storing in an array into a single object. Each one of your previous 6 arrays will be a property.
Inside that object you can implement
- (NSComparisonResult)compare:(YourClass *)otherObject {
return [self.date compare:otherObject.date];
}
You can now sort the array and they will sort by date.

Order two NSMutableArrays based on one

I have two NSMutableArrays. The content of the first is numerically, which is paired to the content of the second one:
First Array Second Array
45 Test45
3 Test3
1 Test1
10 Test10
20 Test20
That's the look of both arrays. Now how could I order them so numerically so they end up like:
First Array Second Array
1 Test1
3 Test3
10 Test10
20 Test20
45 Test45
Thanks!
I would put the two arrays into a dictionary as keys and values. Then you can sort the first array (acting as keys in the dictionary) and quickly access the dictionary's values in the same order. Note that this will only work if the objects in the first array support NSCopying because that's how NSDictionary works.
The following code should do it. It's actually quite short because NSDictionary offers some nice convenience methods.
// Put the two arrays into a dictionary as keys and values
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:secondArray forKeys:firstArray];
// Sort the first array
NSArray *sortedFirstArray = [[dictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
// Sort the second array based on the sorted first array
NSArray *sortedSecondArray = [dictionary objectsForKeys:sortedFirstArray notFoundMarker:[NSNull null]];
Rather than keep two parallel arrays, I'd keep a single array of model objects. Each number from the first array would be the value of one property, and each string from the second array would be the value of the other property. You could then sort on either or both properties using sort descriptors.
Generally, in Cocoa and Cocoa Touch, parallel arrays make work while model objects save work. Prefer the latter over the former wherever you can.

Resources