Sorting an NSMutableDictionary of custom objects whose values may contain duplicates - ios

I have an NSMutableDictionary of custom objects of type "Fraction", which is composed of a numerator variable of type int, and a denominator variable also of type int. I am able to sort all of the values after they have been pulled from the NSMutableDictionary, and put into an NSArray. However, I have just discovered a new problem. Because my NSMutableDictionary is a collection of "Fraction" objects, it is very possible that some of these objects may be duplicates of one another, which means finding the respective "key" value from the original NSMutableDictionary will also cause problems. How do I overcome this? I have an NSMutableDictionary that contains NSStrings for keys, and custom "Fraction" objects for values. I have an NSArray that contains these custom "Fraction" objects, sorted from biggest, to smallest. The problem is that some of these objects may be the same (e.g. 1/2, 1/3, 1/3, 1/4). What I would like to know is how to pull each key for its respective "Fraction" object, despite the fact that there are duplicates?
Thanks in advance to all who reply.

To retrieve all keys for a particular object, use the allKeysForObject: method of NSDictionary.

Related

Persisting NSCountedSet to NSUserDefaults

I need to persist an NSCountedSet of custom objects to NSUserDefaults.
I think the problem is that -(id)objectForKey: for NSUserDefaults has a special consideration in the docs here:
Special Considerations
The returned object is immutable, even if the value you originally set was mutable.
Question
How would I go about persisting an NSCountedSet if (as I am currently assuming?) NSUserDefaults returns a non-mutable NSSet from -(id)objectForKey: when I need to retain the internal count metadata that NSCountedSet contains?
You can't write an NSCountedSet to user defaults. Only arrays, dictionaries, strings, numbers, date and data.
You could create a dictionary matching the NSCountedSet, with the set elements as keys and the counts converted to NSNumber as values. And of course when you read the dictionary, convert it to a counted set. Just a few lines of code.
Alternatively, convert to an array with values duplicated depending on their count. If the counted set contains "Hello" with a count of 3, add it to the array three times.

isEqualToArray or isEqualToDictionary how deep can it compare?

Ohkk So I have many scenarios here.
case:1
A NSArray of dictionaries with a key as type NSString and value also of type NSString.In this case if I change one value in the NSdictionary on the array and try comparing old array with new one it works. isEqualToArray returns false
case:2
A NSArray of dictionaries with a key type as NSString and value type of some model object with attributes like name,address,DOB . So if I change one value in the model object like name and insert in the dictionary with same key. And compare the arrays with old one still works. isEqualToArray returns false
Now this can go on .What if I have a NSArray in my model object which of again a primitive type of some model.What will happen?? Does isEqualToArray compares almost everything in the values of the objects like deep-serializing compare or it has to stop somewhere??
When you compare arrays, the NSArray isEqual: method first checks that both arrays have the same number of elements (otherwise, they are obviously not the same), and then it goes through all the elements one by one and compares them in turn using the isEqual: method. So if your array contains other arrays, or dictionaries, or other objects, then arrays are again compared as just described, dictionaries will be compared as I will describe, and other objects are compared by sending isEqual.
When you compare dictionaries, the NSDictionary isEqual: method first checks both dictionaries have the same number of key/value pairs. Then it takes the first key of the first dictionary, and that key must be present in the second dictionary, and the objects must be the same. Then the second key, the third key and so on.
It all works as long as each class involved has a proper implementation of the isEqual: method. It really has nothing to do with isEqualToArray:. All that does is call isEqual: on each object in the two arrays. So it depends on those objects having a valid isEqual: method (and hash method).
As long as your model object's isEqual: method properly compares each of its properties, you will get the expected result.

What is the most reliable and fastest type or data structure to store data using Objective C?

I am looking for the type or data structure to store a big number of same type primitives on my app (Mac OS X or iOS) using Objective C. As I understood NSNumber stores only one primitive (correct me if I am wrong). I have, let's say, thousands of integers or strings. Which would be the best solution to put, store and access them there? NSSet, NSArray, NSMutableArray, NSDictionary, NSMutableDictionary or something else? I know that they have different features, but I care basically only about the performance of basic operations (putting, storing, retrieving).
It only depends on how you want to ADD, STORE and REMOVE this data.
First Let us go through each type of Data Structure that is available to us in Objective-C:
Primitive Array
This is the most basic type of storage in Objective-C(or C) which is used to store primitives.
Ex: int a[4] = {1, 2, 3, 4};
The limitation to this is
Can only store primitive types.
Array size cannot be changed once declared.
Can only be retrieved by its index.
Can store only single type of data, defined at the time of declaring the array.
NSArray
This is a container for storing objects. Any object which is of type NSObject (or inherits from NSObject) or is of type 'id' can be stored in NSArray.
Once initialized, it cannot be mutated i.e. array size cannot be changed nor the objects it contains can be modified. This is good in terms of security.
Objects can only be accessed by its index.
NSMutableArray
Same as NSArray, but
Can be mutated, i.e. the existing objects can be modified and also new objects can be added or deleted.
NSSet
Same as NSArray but
Stores only unique objects.
Objects cannot be accessed by its index. Objects can only be accessed by enumeration.
NSMutableSet
Same as NSSet, but
Can be mutated, i.e. objects can be added or removed at a later point of time.
NSOrderedSet
Same as NSArray, i.e. objects are stored and retrieved by an index, but
Stores only unique objects.
NSMutableOrderedSet
Same as NSMutableArray, but
Stores only unique objects.
NSDictionary
Can store any type of data.
Objects are stored and retrieved by a key.
Once initialized, cannot be mutated i.e. cannot add new key-values nor can update existing objects associated to a particular key.
NSMutableDictionary
Same as NSDictionary
Can be mutated, i.e. new objects can be added or removed and existing objects can be modified.
This was a short description about mostly used Data Structures in Objective-C. These are used based on the need of the program and how data is to be manipulated.
Therefore,
If you want to store thousands of numbers and strings and want access it by its index value then use NSMutableArray. If you are not going to add, remove or modify any objects in the future then use NSArray.
If you want to store data but do not want duplicates and want to access it by its index the use NSOrderedSet/NSMutableOrderedSet
If you want to store data but do not want duplicates and its order also doesn't matter then use NSSet/NSMutableSet.
If you want to access data by a particular key then use NSDictionary/NSMutableDictionary
Regarding Performance
Since NSSet doesn't contain any order, they are more performant than NSArray
Here is a very good and detailed article on performance characteristics for each Data Structure discussed above
Class Time [ms] 1,000,000 elements
Adding
NSMutableOrderedSet 3190.52
NSMutableDictionary 2522.47
NSMutableSet 2511.96
NSMutableArray 1423.26
NSSet 8.03
Random Access
NSMutableOrderedSet 10.74
NSMutableDictionary 9.18
NSMutableArray 8.08
NSMutableSet 4.47
NSSet 3.56
To know more about Objective-C Data Types and Data Structure, read this

NSArray vs NSDictionary - Which is better for string searching

I am going to store a list of values in a plist and retrieve them. The list will be searched for a value (which may change each time) each time the search method is called. There will probably be about 10 values in the list.
Is either NSArray or NSDictionary better for a search?
I think that NSArray is more appropriate because right now, at least, I don't have a key-value pair data set, just a list.
Could someone confirm and perhaps offer the best method for search the array?
Thanks
The problem is conceptual:
If you have a list of value, you mustn't use NSDictionary. The appropriate data structure is NSArray or NSSet.
Basically, NSSet is faster than NSArray, because doesn't have to consider the order etc.
So if you need just to search a value and the order doesn't matter, the best data structure to use is NSSet (or NSMutableSet if you need it mutable).
Dictionaries will be faster for searching than arrays because the keys are hashed. With an array the system has to check all the items until it finds a match.
The docs for NSSet say that sets are faster in testing for membership than arrays. If you don't have values to store for each key, the best option might be to store the data to your plist as an array. At runtime, read the array, and load it's contents into a set using the NSSet class method setWithArray:
If your data sets are large (many thousands of items) I would suggest doing performance testing with dictionaries where all the values are NSNull against an NSSet. (With a dictionary you'd use objectForKey: and with a set you'd use the containsObject: method
Ignoring timing the best option would be to use an NSArray. NSArray has all sorts of useful methods such as
(NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
and
(NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
that you can use for searching within an array.

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

Resources