9

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

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
Kunal Balani
  • 4,739
  • 4
  • 36
  • 73

5 Answers5

16
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)
Community
  • 1
  • 1
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
5

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.
Tricertops
  • 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
-1

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.

Pedro Mancheno
  • 5,237
  • 4
  • 24
  • 31
-1
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.

Mehul Mittal
  • 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
  • @GabrielePetronella True, he is missing `-copy`. – Tricertops Oct 15 '13 at 21:38
  • @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
  • @GabrielePetronella We all know that, but he was just trying :) – Tricertops Oct 15 '13 at 21:43
  • @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
-2
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];
Hot Licks
  • 47,103
  • 17
  • 93
  • 151