isEqualToArray or isEqualToDictionary how deep can it compare? - ios

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.

Related

NSDictionary: is order of iteration preserved?

Say I want to access all objects in some NSDictionary object.
I want to iterate like this:
for (key in dict){}
Is it guaranteed that for each run the objects in the dictionary will be accessed in the same order?
PS. Let me explain my question more thoroughly: if I iterate dictionary once and access keys in some concrete order - will I have the same order on second iteration attempt?
Sorting of keys is not guaranteed because of the nature a key is placed inside the NSDictionary. But that is only the half answer. read on...
If we iterate thru the dict with a for (NSString* key in dict) loop or even more unspecific with for (id<NSCopying> key in dict) loop then we use actually NSEnumeration. Those kind of iterations are unpredictable to you. NSEnumeration and NSFastEnumeration do not iterate the dict with indexes. They go thru by addresses or hashes of keys, so to speak. Thats also a blurry answer that is not complete.
Keys inside a dict are unique compared to other keys in the same dict.
Which is the great thing and why you would use a NSDictionary or NSMutableDictionary instead of indexed NSArray or NSPointerArray or unspecific NSSet, and specially offered datatypes as NSOrderedSet, NSOrderedCollection. NSMapTable and NSHashTable tend to behave like NSDictionaries but they have a completely different way how they store the keys and how they iterate.
So what happens when you rewrite a keyed value in NSDictionary?
NSDictionary *dict = #{
#"A" : #(1234),
#"B" : #(4321)
}
NSMutableDictionary *mutabledict = [dict mutablecopy];
mutabledict[#"A"] = #(5678); //beware this only works on NSMutableDictionary.
dict = mutabledict;
here we exchange the value of the first declared key A.
mutabledict[#"A"] [key] is a getter subscript used to find the address of the keys value pair. Nothing is changed on the key itself. So the order of keys stays as it was the moment the unique key was copied in.
You need to be careful when you create a mutablecopy, because then whole pairs are copied. The outcome in sorting may be unpredictable as well.
Simple: But Accessing keys value does not change their memory layout.
But: NSDictionary are immutable, so the keys can not be changed once they are set. So you can say:
The keys are ordered in the order they where stored the first time. But you can't access the values stored in a guaranteed order when you don't know the order the keys:value pairs where placed and with this process its unique keys copied into the dict.
If you can't control the order the keys are set then the keys order is unknown to you, (respecting the question) not undefined. And NSEnumeration iteration gives you not a real picture of the order they are stored.
How to deal with that?
The easies way to get known ordered sorting of keys is by manually sorting all its keys like..
NSArray *sortedKeys = [dict.allKeys sortedArrayUsingSelector:#selector(compare:)];
which is giving you ascending order of keys and lets you iterate thru your dict with arrays indexes containing addresses to keys. like ..
for (unsigned int i=0; i<sortedKeys.count; i++) {
NSString* key = sortedKeys[i];
dict[ key ] = yourValue;
}
if keys are not of interest but the guaranteed sorting is more important to you, then you could convert the NSDictionary into a sorted NSArray without keys and access its indexes in a loop. With the obvious back-draw to have no keys unless you store them manually.
id notfoundmarker = #"empty";
NSArray *sortedkeys= [dict.allKeys sortedArrayUsingSelector:#selector(compare:)];
NSArray *oneForOneSorted = [dict objectsForKeys:sortedkeys notFoundMarker:notfoundmarker];
for (unsigned int i=0; i<oneForOneSorted.count; i++) {
id<NSObject> value = oneForOneSorted[i];
NSLog(#"%#", value);
};
My question was different: if I iterate dictionary once and access keys in some concrete order - will I have the same order on second iteration attempt?
Short Answer
In all probability yes
Longer Answer
How a dictionary is constructed internally is not specified, and there are multiple ways to represent dictionaries, all that is known is that key values must be hashable which implies hashing is used somehow.
It is also not know what algorithm a dictionary uses to provide the keys when enumerating them, and again for an particular possible representation there could be more than one enumeration algorithm possible.
So we have a lot of unknowns.
What do we know?
In the absence of threading, random number generation, and anything similar an Objective-C program is deterministic, a trait it has in common with C, Swift, Java, C# and a host of other languages.
NSDictionary is not thread-safe so its unlikely to use threading in its implementation.
And why would it use random numbers?
NSDictionary is also an immutable type so once constructed there is no obvious reason to re-order its internal storage in response to calls querying its contents.
So, in all probability, you will get the same key order on every enumeration.
Without the source though you can't be certain, you cannot prove the absence of something by black-box testing. Maybe the programmer decided that every millionth enumeration they'd throw a little randomness in – just for fun ;-) Is this likely? Maybe not (its probably a good way to get fired!), but it's not impossible.
If you ask out of curiosity, then good stay curious!
If however you want to rely on the order being deterministic for code correctness then sort the keys (into a peculiar order if you wish as long as its deterministic), the cost of doing so will in all probability be inconsequential.
HTH
The docs state that for the allKeys property the order of the elements in the array is not defined.
So you can just sort the keys array to ensure it's always sorted.
Each iteration will be in a random order because Swift dictionaries don't prioritize order. Arrays do, however, so you can sort the dictionary (by key or value) to produce an array of tuples, which in effect can be treated like a sorted dictionary.
let dictionary = ["k4": 3, "k2": 8, "k1": 6]
let sortedArrayOfTuples = dictionary.sorted(by: { $0.key < $1.key })
for entry in dictionary {
print(entry.key)
}
for entry in sortedArrayOfTuples {
print(entry.key)
}

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.

Get index for value NSDictionary

I've got a value like so: #"2329300" and I've got a NSDictionary like so :{#"John Appleseed":[#"2329300",#"2342322",#"32i249"]}
How do I find the index of the key/value pair in the NSDictionary when I've only got a string value of the entire list that's known as the value. I'm assuming there's no duplicates in the dict.
I know that there's indexForObject on a NSArray but is there a similar thing for a dict?
I imagine it would look something like this:
[NSDictionary indexForValue:value]; // returns index number.
And even then the NSString doesn't match the value, so I'd need a workaround for that too.
You have a basic misunderstanding. Dictionaries are unordered collections. They do not have any particular order for their key/value pairs. You can't have indexes to the key/value pairs because that implies a fixed order.
Think of a dictionary as a bunch of kids milling around on a playground. You can call out a kid's name "Johnny, come here!" and fetch that kid (use a key to find an object) but what does order mean for kids that won't sit still?
You can create an array of the keys from a dictionary and sort that into a particular order (an alphabetical list of the kids on the playground) if that's what you want, or you can create an array of dictionaries, or an array of a custom data object that contains any arbitrary properties that you want.
EDIT:
For a table view, an array of dictionaries is probably a good choice. Each entry in the array contains a dictionary with all the settings for a cell in the dictionary. If you have a sectioned table view then you want an outer array for sections, containing inner arrays for the rows, and each entry in the inner array containing a dictionary.
I tend to prefer custom data objects to dictionaries though. (An object that just has properties for each setting I want.) That way the list of values and their types is crystal-clear and fairly self-documenting.

Availability of bidictionary structure?

I'm facing a case in my application where I need a bidirectional dictionary data structure, that means a kind of NSDictionary where your can retrieve a key with a value and a value with a key (all values and keys are unique).
Is there such a kind of data structure in C / ObjectiveC ?
You can do it with a NSDictionary:
allKeysForObject: Returns a new array containing the keys
corresponding to all occurrences of a given object in the dictionary.
(NSArray *)allKeysForObject:(id)anObject Parameters anObject The value to look for in the dictionary. Return Value A new array
containing the keys corresponding to all occurrences of anObject in
the dictionary. If no object matching anObject is found, returns an
empty array.
Discussion Each object in the dictionary is sent an isEqual: message
to determine if it’s equal to anObject.
And:
objectForKey: Returns the value associated with a given key.
(id)objectForKey:(id)aKey Parameters aKey The key for which to return the corresponding value. Return Value The value associated with
aKey, or nil if no value is associated with aKey.
Literally, the answer is No.
As a workaround you may create a helper class which manages two dictionaries.
Another approach is to create a thin wrapper around C++ container which implement this: boost's Bimap.
When using ARC and Objective-C objects as values or keys in C++ containers, they will handle NSObjects quite nicely. That is, they take care of memory management as you would expect - and you even get "exception safety" for free. Additionally, C++ standard containers are also a tad faster, use less memory, and provide more options to optimize (e.g. custom allocators).

Sorting an NSMutableDictionary of custom objects whose values may contain duplicates

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.

Resources