1

I tried to lookup for this issue but with no luck, I want to build a function that creates a "Immutable Dictionary" of Keys and Objects where the object is also an Immutable Array.

What I will pass to this function is an array of objects I created, each object has a key property that I want to use to group the objects in the dictionary.

I came up with this and I tested it and it works but I want to see if there is a better/safer way to do this.

I do use ARC and I want to make sure every thing is immutable when i return from the function.

- (NSDictionary* )testFunction:(NSArray *)arrayOfObjects
{
    NSMutableDictionary *tmpMutableDic = [[NSMutableDictionary alloc] init];

    for(MyCustomObject *obj in arrayOfObjects)
    {
        if ([tmpMutableDic objectForKey:obj.key] == nil)
        {
            // First time we get this key. add key/value paid where the value is immutable array
            [tmpMutableDic setObject:[NSArray arrayWithObject:obj] forKey:obj.key];
        }
        else
        {
            // We got this key before so, build a Mutable array from the existing immutable array and add the object then, convert it to immutable and store it back in the dictionary.
            NSMutableArray *tmpMutableArray = [NSMutableArray arrayWithArray:[tmpMutableDic objectForKey:obj.key]];
            [tmpMutableArray addObject:obj];
            [tmpMutableDic setObject:[tmpMutableArray copy] forKey:obj.key];
        }
    }

   // Return an immutable version of the dictionary.
   return [tmpMutableDic copy];
}
1
  • What's the need for an immutable dictionary? Commented Aug 22, 2013 at 20:20

2 Answers 2

2

I think that's a lot of copying. I'd wait until the end to convert the mutable arrays to immutable arrays rather than copying it each time you want to add an element:

- (NSDictionary *)testFunction:(NSArray *)arrayOfObjects
{
    NSMutableDictionary *tmpMutableDic = [[NSMutableDictionary alloc] init];

    for(MyCustomObject *obj in arrayOfObjects)
    {
        if ([tmpMutableDic objectForKey:obj.key] == nil)
        {
            // First time we got this key, add array
            [tmpMutableDic setObject:[[NSMutableArray alloc] init] forKey:obj.key];
        }
        // Add the object
        [[tmpMutableDic objectForKey:obj.key] addObject:obj];
    }

    // Convert mutable arrays to immutable
    for (NSString *key in tmpMutableDic.allkeys) {
        [tmpMutableDic setObject:[[tmpMutableDic objectForKey:key] copy] forKey:key];
    }

    // Return an immutable version of the dictionary.
    return [tmpMutableDic copy];
}
Sign up to request clarification or add additional context in comments.

5 Comments

I agree 100% I just wanted to do it in one loop :) Do you see any other issues with the code? memory issues?
@user2708681, I fixed an error when converting the arrays. Have to use setObject. No memory issues here, with ARC.
this code throw runtime when i execute second loop: was mutated while being enumrated
fixed by using for (NSString *key in tmpMutableDic.allkeys)
I kept the conversion in one line and added allKeys and it passed all my unit tests, i will mark this as best answer so far.
0

There's no built-in concept of a one-to-many dictionary so I don't think there's a neater way to achieve exactly what you write — there's certainly no single line of syntax like there is for a whole bunch of other common manipulations.

That said, minor suggestions would be:

Use key-value coding to read the keyed property. So replace your references to obj.key with a single call to [obj valueForKeyPath:keyPath] and take keyPath as a parameter. Then you've got to write this code only once.

For safety you should check that obj.key isn't nil. If it is then you'll end up doing [tmpMutableDic setObject:... forKey:nil], which raises an exception because dictionaries can't store values against a nil key.

copy will return an immutable dictionary but the values in it will still be mutable arrays as copies are shallow. If you explicitly want to return immutable arrays you'll need to write a quick slightly deeper copy function.

At that point you may find that you prefer to switch to an algorithm that sorts the incoming array based on the key and then steps through it creating a new array every time the value of the key changes. The advantage of that would be that you always know the moment you're done with an array so you can store a copy of it to the dictionary then and cut out the slightly deeper copy later on, albeit at the cost of a sort.

2 Comments

Thank you for the info, I do check for nil in the caller of this function when i build the array to pass but i guess i can add that check again in the function to make more defensive. in my else clause i do use NSArray when i set the object for Key, so i am not sure what do you mean my code will return mutable arrays? I tried to access the returned NSDictianary of that function by: [((NSMutableArray *)[theDictionaryThatIGot objectForKey:@"xxxKey"]) addObject:[[MyCustomObject alloc] init]];
theDictionaryThatIGot is the NSDictionary i got from that function. I got an error here so that proves that the array is not Mutable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.