Flatten an array of dictionaries with KVC - objective-c

I'm trying to flatten a two dimensional array of dictionaries with the use of KVC.
NSArray *toBeFlatten = #[#[#{#1:#1}],#[#{#2:#2}]];
NSArray *flat = [toBeFlatten valueForKeyPath:#"#unionOfArrays.self"];
// flat:#[NSNull.null, NSNull.null];
Why are the dictionaries "converted" to NSNull?

It's because valueForKeyPath: also applies to the dictionaries. And how does it work? It looks in each dictionary, tries to use "self" as a key, finds no such key, and returns NSNull to represent the missing value.
To see what I mean, consider this variation on your example:
NSArray *toBeFlatten = #[#[#{#"self":#"hey"}],#[#{#"other":#"ho"}]];
NSArray *flat = [toBeFlatten valueForKeyPath:#"#unionOfArrays.self"];
The result is #[#"hey", NSNull.null] — the value "hey" for the matching key "self" in the first dictionary, and the null because no key matched in the second dictionary.
What you probably meant to say is valueForKeyPath:#"#unionOfArrays.#self" (notice the at-sign, making self an operator, not a key).

Related

How to enumerate through dictionary in Objective-C in the order in which they were defined

Is it possible to enumerate NSDictionary in the order in which the key-value is defined?
There's no built-in way to do this. You'll have to store the order as a property of the added objects or use NSNumber as the keys.
Nope. From the documentation:
allKeys Returns a new array containing the dictionary’s keys.
(NSArray *)allKeys Return Value A new array containing the dictionary’s keys, or an empty array if the dictionary has no entries.
Discussion The order of the elements in the array is not defined.
The only way you can do this is by creating an array of keys first.
Then use that array, iterating over it to create the dictionary.
Dictionaries do not have any inherent concept of order.

Immutable NSDictionary with deeply nested hierarchy: change a value for a key?

If I have an immutable NSDictionary with nested hierarchy, from a JSON string, what is the easiest way to change a value for a key that is deeply nested in the hierarchy?
For example, I have a dictionary, and the value for "key1" is an array, inside the array, each element is a dictionary, and inside each dictionary, there is a value for key "key2", now I want to change the value for "key2", since the whole data structure is immutable, which makes it difficult, should I duplicate this data structure with mutable collection so that I can change that value, this seems to have a lot of overhead, but this is the only way that came into my mind.
I don't know if this is an acceptable alternative for you, but you can create the dictionary from the JSON string with the NSJSONReadingMutableContainers option, which creates all arrays and dictionaries as mutable objects.
i'm not totally sure, but i think you'll have to pass everyone of your dictionary and array to a mutable one.
It depends if you want to still have a immutable structure after the change or if it doesn't matter if it's still mutable after it.
if you want to stay immutable after the change, then you'll have to use temp variable for mutable dict and array.
NSMutableDictionary *rootDict = [NSMutableDictionary dictionaryWithDictionary:rootImmutableDict];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:[rootImmutableDict objectForKey:#"key1"]];
NSMutableDictionary* valueDict = [NSMutableDictionart dictionaryWithDictionary:[mutableArray objectAtIndex:idx]];
[valueDict setObject:newValueObject forKey:#"key2"];
[rootImmutableDict release];
rootImmutableDict = nil;
rootImmutableDict = [[NSDictionary alloc] initWithDictionary:rootDict];
if it doesn't matter for you if it's mutable, then you'd have to make it mutable when retrieving it from the JSON by using a temp immutable structure and make it mutable permanently.
i hope it'll be helpfull to you.

Objective C - difference between dictionaries and arrays?

in Objective C - what is the difference between dictionaries and arrays? Are dictionaries used with key : value (where Key can be any object), and arrays are id : value (where id is an integer)?
thanks
Array
In Objective-C, an array is a type of collection which can store object types. It can store any type of objects. Objects stored in an array are linked to their index number.
eg. if you create an array and insert the first object, it will be stored in "index 0"
and, the index number will keep on increasing from 0,1,2....n
Use "NSMutableArray" to create an array that can be modified.
example,
NSMutableArray *array = [NSMutableArray alloc] init];
[array addObject:#"Tom"];
[array addObject:#"Cat"];
So, at index 0, you will have "Tom". And, at index 1, you will have "Cat".
Dictionary
In Objective-C, a dictionary is a type of collection that stores "key-value" pairs.
The "key" is of type ID, so you can enter any object as key for a value.
Use "NSMutableDictionary" to create a dictionary that can be modified.
example,
NSMutableDictionary *dictionary = [NSMutableDictionary alloc] init];
[dictionary setObject:#"Tom" forkey:#"name"];
[dictionary setObject:#"Cat" forKey:#"animal"];
The key difference between array and dictionary is the sequence of the objects gets changed in a dictionary, while in an array the sequence of objects stored is SEQUENTIAL.
[EDIT]
Since there has been quite a discussion with regard to this answer, I will make it clear that the array does NOT get re-created as some comments say.
The size of array/dictionary gets dynamically increased to accomodate the new elements in the colletion.

Is it possible to use KVO to obtain a sub-array of an array?

I have an array of objects which have an enum as one of their properties, I would like to obtain a filtered array based upon the value of the enum i.e. the returned array contains only objects which have a specified enum value.
I was wondering if KVO could be used as a tidy way of doing this, but haven't found anything suggesting it is?
You can do this by filtering the array using a predicate:
NSArray * filteredArray = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"nameOfProperty == %d", theEnumValue]];
The string for the predicate names the property you're interested in, the value to which it should be compared, and the relationship the two must have for the predicate to evaluate as true.

Can I change an NSDictionary's key?

I have an NSDictionary object that is populated by instances of NSMutableString for its keys and objects. I have been able to change the key by changing the original NSMutableString with the setString: method. They key however remains the same regardless of the contents of the string used to set the key initially.
My question is, is the key protected from being changed meaning it will always be the same unless I remove it and add another to the dictionary?
Thanks.
The keys are -copy'd when the items are set, so you can't changing it afterwards is useless.
Methods that add entries to dictionaries—whether as part of initialization (for all dictionaries) or during modification (for mutable dictionaries)—copy each key argument (keys must conform to the NSCopying protocol) and add the copies to the dictionary. Each corresponding value object receives a retain message to ensure that it won’t be deallocated before the dictionary is through with it.
You could use CFDictionary with kCFTypeDictionaryKeyCallBacks, or just replace the item:
id value = [dictionary objectWithKey:oldKey];
[dictionary setObject:value withKey:newKey];
[dictionary removeObjectForKey:oldKey];
Try using NSMutableDictionary, instead.
You can create a copy of the dictionary, filtering the keys as you go. I do this for converting between camel-case and underscores when populating objects from JSON using KVC. See my refactoring library, es_ios_utils, for source. ESNSCategories.h provides:
#interface NSMutableDictionary(ESUtils)
//Changes keys using keyFilter. If keyFilter generates duplicate non-unique keys, objects will be overwritten.
-(void)addEntriesFromDictionary:(NSDictionary*)d withKeyFilter:(NSString*(^)(NSString*))keyFilter;
...
So to make all keys upper case, you could do something like:
NSMutableDictionary *md = [NSMutableDictionary dictionaryWithCapacity:oldDictionary.count];
[md addEntriesFromDictionary:oldDictionary
withKeyFilter:^NSString*(NSString *key) {
return key.uppercaseString;
}];