I have a NSMutableDictionary
and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString
objects

- 106,943
- 21
- 217
- 235

- 4,739
- 4
- 36
- 73
-
Is there a guarantee that there are no keys which duplicate values or vice-versa? – Hot Licks Oct 15 '13 at 18:13
-
Yes, Keys are auto generated id's and values are strings. – Kunal Balani Oct 15 '13 at 18:15
5 Answers
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:@{
@"key1" : @"value1",
@"key2" : @"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(@"%@", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
- unique values (as they will become keys)
- values conform to
NSCopying
(same as above) - no value is equal to any key (otherwise colliding names will be lost in the process)

- 1
- 1

- 106,943
- 21
- 217
- 235
-
-
@Jim of course, but the OP guaranteed key/valye uniqueness in the question – Gabriele Petronella Oct 15 '13 at 18:00
-
1I don't think you want to modify a collection while iterating over it. – Pedro Mancheno Oct 15 '13 at 18:00
-
11The enumerated collection is `[d allKeys]`, and that is *not* mutated during the enumeration. – Martin R Oct 15 '13 at 18:03
-
Also, this won't work if the value doesn't conforms to the NSCopying protocol. – Pedro Mancheno Oct 15 '13 at 18:58
-
1@pedro.m, the OP stated that they are `NSString`, so it conforms to `NSCopying` – Gabriele Petronella Oct 15 '13 at 19:24
-
Check my updated answer. It handles all cases. No assumptions made. – Pedro Mancheno Oct 15 '13 at 19:50
-
@pedro.m Checked and it looks ok. See my comment below it. By the way I don't know whether it was you, but I'd like to know the reason of the downvote on my answer. – Gabriele Petronella Oct 15 '13 at 19:55
-
It was because of the NSCopying, but I see you have fixed that so I'll update. – Pedro Mancheno Oct 15 '13 at 20:06
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.

- 8,492
- 1
- 39
- 41
-
As many of the other answers, it misses the main point: it's not in place. – Gabriele Petronella Oct 15 '13 at 20:20
-
2@GabrielePetronella Sorry. But if you have performance or memory issues, maybe you could just use `-allKeysForObject:` to do inversed lookup. No allocations, no copying. – Tricertops Oct 15 '13 at 20:24
-
it's not me, it's the OP that requires it in the question. – Gabriele Petronella Oct 15 '13 at 21:30
-
@GabrielePetronella I see. I understood “in place” little differently. In the end, if you do `[d setDictionary:invertedDictionary];` you have it in place, or not? :) – Tricertops Oct 15 '13 at 21:40
-
Not quite, in the sense that you are allocating extra memory for creating `invertedDictionary`, which is what the OP doesn't want. – Gabriele Petronella Oct 15 '13 at 21:41
-
@GabrielePetronella Edited the answer. Like this, it should not go up with memory and I'm reusing the original dictionary. In addition, this solution works for immutable dictionaries as well. – Tricertops Oct 15 '13 at 21:50
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
- Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
- Transverses the whole hierarchy of a collection, swapping all the keys
- It's fast since it uses recursion and fast enumeration
- Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
@interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
@end
@interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:@protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
@end
and...
@interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
@end
@implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
@end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = @{@"1" : @"a",
@"2" : @[ @{ @"5" : @"b" } ],
@"3" : @{@"6" : @"c"},
@"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict
object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>"
represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.

- 5,237
- 4
- 24
- 31
-
I didn't go through all the code, but it looks like an overkill for the task – Gabriele Petronella Oct 15 '13 at 17:57
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption: No key = value;
Complexity: No extra space required. Will loop through once and replace all key value pairs.

- 147
- 2
- 9
-
1-1 This doesn't even compile. `getObjectForKey` does not exist, and you cannot loop through a dictionary like that. That said, even if you fix the obvious errors, the core concept it's identical to the accepted answer, so it's doesn't add any value. – Gabriele Petronella Oct 15 '13 at 20:13
-
1@GabrielePetronella Just a detail: you _can_ loop through the dictionary just like this. – Tricertops Oct 15 '13 at 20:45
-
@GabrielePetronella Sorry about the mistake, I forgot its objectForKey. And I can loop through a dictionary with for(NSStrig *key in myDictionary) where myDictionary is a valid dictionary. – Mehul Mittal Oct 15 '13 at 20:47
-
1@iMartin yes my bad, even though it's still a bad idea to mutate a collection while iterating on it. – Gabriele Petronella Oct 15 '13 at 21:28
-
-
@iMartin or simply iterate on the key collection as in my answer. That's why I said that this answer will eventually be a clone of mine. ;) – Gabriele Petronella Oct 15 '13 at 21:40
-
-
@iMartin fair enough, the answer is still worth a downvote, but I'll be happy to remove it as soon as it's fixed. – Gabriele Petronella Oct 15 '13 at 21:51
-
@GabrielePetronella i see what you mean. :) it would have to iterate though all the keys of the dictionary and that makes it a replica of your answer. – Mehul Mittal Oct 16 '13 at 00:28
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];

- 47,103
- 17
- 93
- 151
-
-
8You probably meant to swap allKeys with allValues? In any case, the order of the elements returned by `allValues` is not defined, therefore this might associate the wrong elements. – Martin R Oct 15 '13 at 18:13