Creating an NSDictionary from an NSArray (Objective-c 2.0) - objective-c

I have an NSArray containing n elements at indices 0, 1 ... n-1. I want to populate an NSDictionary with the contents of my array.
Specifically the dictionary should contain key-value pairs where the key is the hash of the ith element in the array and the value is the index into the array.
For example: array = [123, 101, 199] then the dictionary will contain three key-value pairs:
([123 hash], 0)
([199 hash], 2)
([101 hash], 1)
I've done this with a for loop over the array. What's a more concise way to do this? Perhaps something from NSKeyValueCoding?
More info: I'm thinking of something like this:
NSArray *keys = [myArray valueForKey:#"hash"];
NSArray *values = [myArray valueForKey:#"index"]; // #"index" needs to change
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:values
forKeys:keys];

I would probably use something like David's for loop solution myself, but just for kicks, and because I'm still trying to wrap my head completely around them, I came up with a solution using blocks:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[array count]];
[array enumerateObjectsUsingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
[dict setObject:[NSNumber numberWithUnsignedLong:idx] forKey:hashFor(obj)]; } ];
I'm assuming the existence of a function hashFor that generates the hash. You can replace that part with a message to obj or whatever you do to generate the hash.

A for loop is pretty good, especially if you need the index. Otherwise you might use fast enumeration:
int i = 0;
for (object in array) {
… [NSNumber numberWithInt: i] … // Add to dict
i++;
}
This has nothing to do with KVC.

Related

componentsSeparatedByString - convert to array of NSMutableStrings [duplicate]

How to do that without having to "scroll" the entire given array with a "for" loop?
The best I can come up with is:
NSMutableArray *replacementArray = [NSMutableArray array];
[originalArray enumerateObjectsUsingBlock:
^(id obj, NSUInteger index, BOOL *stop)
{
[replacementArray addObject:[[obj mutableCopy] autorelease]];
}
];
Which more or less just tells the originalArray to construct the for loop for you. And if anything it's more work than:
NSMutableArray *replacementArray = [NSMutableArray array];
for(id obj in originalArray)
[replacementArray addObject:[[obj mutableCopy] autorelease]];
Since nobody seems to agree with my comment that this is a duplicate of Better way to convert NSArray of NSNumbers to array of NSStrings, here's the same answer again:
NSArray * arrayOfMutableStrings = [arrayOfStrings valueForKey:#"mutableCopy"];
From the docs:
valueForKey:
Returns an array containing the results of invoking
valueForKey: using key on each of the array's objects.
- (id)valueForKey:(NSString *)key
Parameters
key The key to retrieve.
Return Value
The value of the retrieved key.
Discussion
The returned array contains NSNull elements for each object that returns nil.
I wrote a dictionary method on NSArray to be able to write cleaner functional code
-(NSArray *)arrayByPerformingBlock:(id (^)(id))performBlock
{
NSMutableArray *array = [NSMutableArray array];
for (id element in self)
[array addObject:performBlock(element)];
return [NSArray arrayWithArray:array];
}
usage:
arrayWithStrings = [arrayWithStrings arrayByPerformingBlock:^id(id element) {return [[element mutableCopy] autorelease];}];
This was inspired by list comprehensions I know from Python. I also wrote versions of this methods with testing. See my arraytools.

NSDictionary order of keys and values

I know that in NSDictionary, there are no guarantees about the order of items when enumerating.
However, is it safe to expect that [[myDictionary allValues] objectAtIndex:index] will always be the matching value of key [[myDictionary allKeys] objectAtIndex:index]? (supposing that the dictionary is immutable).
Even if there is a very good chance that the two are going to be in the same order, Apple can change it at any time, because there is simply no guarantee of the order.
If you need two NSArrays, one with keys and one with values, with "parallel" indexes, a better approach to this would be building these arrays yourself by enumerating key-value pairs of the dictionary, and adding them to the two arrays that you build:
NSMutableArray *keys = [NSMutableArray array];
NSMutableArray *vals = [NSMutableArray vals];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[keys addObject:key];
[vals addObject:obj];
}];
You can also get keys separately, and then get values in an array with parallel indexing by calling objectsForKeys:notFoundMarker: method:
NSArray *keys = [dict allKeys];
// In the call below, the marker [NSNull null] will not be used, because we know
// that all keys will be present in the dictionary.
NSArray *vals = [dict objectsForKeys:keys notFoundMarker:[NSNull null]];

Achieve NSArray merging more elegantly with enumerateObjectsUsingBlock:?

My challenge this week has been to come to terms with blocks in objective-c. There is something about the syntax that does my head in. Getting there.
I have the following code to achieve a merge of two arrays in a specific way (see comment in code below).
NSArray *keys = #[#"name", #"age"];
NSArray *data = #[
#[#"mark", #"36 years"],
#[#"matt", #"35 years"],
#[#"zoe", #"7 years"]
];
// desired outcome is
// # { #"name" : #[#"mark", #"matt", #"zoe"],
// #"age" : #[#"36 years", #"35 years", #"7 years"]
// }
NSMutableArray *mergedData = [[NSMutableArray alloc] initWithCapacity:keys.count];
for (NSString *key in keys) {
NSLog(#"key: %#", key);
NSInteger keyIndex = [keys indexOfObject:key];
NSMutableArray *dataItemsForKey = [[NSMutableArray alloc] initWithCapacity:data.count];
for (NSArray *row in data) {
// double check the array count for row equals the expected count for keys - otherwise we have a 'match up' issue
if (row.count == keys.count) {
[dataItemsForKey addObject:[row objectAtIndex:keyIndex]];
}
}
[mergedData addObject:[NSDictionary dictionaryWithObject:dataItemsForKey forKey:key]];
}
NSLog (#"mergedData: %#", mergedData);
While this code works fine, in the interest of my challenge and learning, I was wondering if there is a more 'elegant' (aka less code, easier to read) way to do this using enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) ??
I can't quite see a way to make it work, but in the interests of self-education, wonder if those more learned in blocks and arrays may have a more elegant solution.
The first issue that I notice is that you are asking for the index of the current object while enumerating the array. This is a waste of operations, because at every loop iteration you have to look over all array elements (potentially O(N)) to find where the object is.
You could instead do this:
for(NSUInteger i=0; i<keys.count; i++)
{
NSString* key= keys[i];
<Rest of the code>
}
Or just keep track of the index manually incrementing it:
NSUInteger i=0;
for (NSString *key in keys)
{
<Your code>
i++;
}
Or like you wanted, with enumerateObjectsUsingBlock:, which is IMO the most elegant way to do it in this case. Here is an example:
NSMutableDictionary* dict=[NSMutableDictionary new];
[keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSMutableArray* fields=[NSMutableArray new];
for(NSArray* array in data)
{
[fields addObject: array[idx]];
}
[dict setObject: fields forKey: obj];
}];
In the case you haven't understood how it works, here is a further explanation:
This way at every execution of the block you can know which is the current object (obj) and it's index (idx). stop is just used to stop enumerating the array, but you don't need it in this case (say that you want to stop the enumeration, you set *stop=YES). In my code I just took every element at the index idx of data, and build an array which is the value that I put into the dictionary, that has obj (what you called key in your code) as key. For any further doubt feel free to ask any clarification through a comment.
The first thing to say is your code does not produce the desired output. You get an array with two dictionaries each with one key.
One way to solve the problem is like this:
NSMutableDictionary* mergedData = [[NSMutableDictionary alloc] init];
[keys enumerateObjectsUsingBlock: ^(id key, NSUInteger keyIndex, BOOL *stop)
{
NSMutableArray* keyValues = [[NSMutableArray alloc] init];
for (NSArray* row in data)
{
[keyValues addObject: [row objectAtIndex: keyIndex]];
}
[mergedData setObject: keyValues forKey: key];
}];
The above will throw an exception if a row doesn't have enough objects in it. You could either check it beforehand or allow the program to crash, it's up to you.

convert NSArray of NSString(s) to NSArray of NSMutableString(s)

How to do that without having to "scroll" the entire given array with a "for" loop?
The best I can come up with is:
NSMutableArray *replacementArray = [NSMutableArray array];
[originalArray enumerateObjectsUsingBlock:
^(id obj, NSUInteger index, BOOL *stop)
{
[replacementArray addObject:[[obj mutableCopy] autorelease]];
}
];
Which more or less just tells the originalArray to construct the for loop for you. And if anything it's more work than:
NSMutableArray *replacementArray = [NSMutableArray array];
for(id obj in originalArray)
[replacementArray addObject:[[obj mutableCopy] autorelease]];
Since nobody seems to agree with my comment that this is a duplicate of Better way to convert NSArray of NSNumbers to array of NSStrings, here's the same answer again:
NSArray * arrayOfMutableStrings = [arrayOfStrings valueForKey:#"mutableCopy"];
From the docs:
valueForKey:
Returns an array containing the results of invoking
valueForKey: using key on each of the array's objects.
- (id)valueForKey:(NSString *)key
Parameters
key The key to retrieve.
Return Value
The value of the retrieved key.
Discussion
The returned array contains NSNull elements for each object that returns nil.
I wrote a dictionary method on NSArray to be able to write cleaner functional code
-(NSArray *)arrayByPerformingBlock:(id (^)(id))performBlock
{
NSMutableArray *array = [NSMutableArray array];
for (id element in self)
[array addObject:performBlock(element)];
return [NSArray arrayWithArray:array];
}
usage:
arrayWithStrings = [arrayWithStrings arrayByPerformingBlock:^id(id element) {return [[element mutableCopy] autorelease];}];
This was inspired by list comprehensions I know from Python. I also wrote versions of this methods with testing. See my arraytools.

Sorting NSDictionary

I was wondering if someone can show me how to sort an NSDictionary; I want to read it starting from the last entry, since the key is Date + Time and I want to be able to append it to an NSMutableString. I was able to read it using an enumerator but I don't get the results I want.
Thanks
For your requirements the easiest way is to create a new array from the keys, sort that, then use the array to reference items from the original dictionary.
(Note myComparison is your own method that will compare two keys).
NSMutableArray* tempArray = [NSMutableArray arrayWithArray:[myDict allKeys]];
[tempArray sortedArrayUsingSelector:#selector(myComparison:)];
for (Foo* f in tempArray)
{
Value* val = [myDict objectForKey:f];
}
I've aggregated some of the other suggestions on StackOverflow on sorting an NSDictionary into something very compact that will sort alphabetically. I have a Plist file in 'path' that I load in to memory and want to dump.
NSDictionary *glossary = [NSDictionary dictionaryWithContentsOfFile: path];
NSArray *array1 = [[glossary allKeys] sortedArrayUsingDescriptors:#[ [NSSortDescriptor sortDescriptorWithKey:#"" ascending:YES selector:#selector(localizedStandardCompare:)] ]];
for ( int i=0; i<array1.count; i++)
{
NSString* key = [array1 objectAtIndex:i];
NSString* value = [glossary objectForKey:key];
NSLog(#"Key=%#, Value=%#", key, value );
}