Глубокий комбинат NSDictionaries

Мне нужно объединить два NSDictionary в один, при условии, что если в словарях есть словари, они также объединяются.

Более или менее нравится функция jQuery extend.

Ответы

Ответ 1

NSDictionary + Merge.h

#import <Foundation/Foundation.h>

@interface NSDictionary (Merge)

+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2;
- (NSDictionary *) dictionaryByMergingWith: (NSDictionary *) dict;

@end

NSDictionary + Merge.m

#import "NSDictionary+Merge.h"

@implementation NSDictionary (Merge)

+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
    NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];

[dict2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
    if (![dict1 objectForKey:key]) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
            [result setObject: newVal forKey: key];
        } else {
            [result setObject: obj forKey: key];
        }
    }
}];

    return (NSDictionary *) [[result mutableCopy] autorelease];
}
- (NSDictionary *) dictionaryByMergingWith: (NSDictionary *) dict {
    return [[self class] dictionaryByMerging: self with: dict];
}

@end

Ответ 2

Я думаю, что это то, что вы ищете:

Сначала вам нужно сделать глубокую изменчивую копию, поэтому вы можете создать категорию на NSDictionary, чтобы сделать это:

@implementation NSDictionary (DeepCopy)

- (id)deepMutableCopy
{
    id copy(id obj) {
        id temp = [obj mutableCopy];
        if ([temp isKindOfClass:[NSArray class]]) {
            for (int i = 0 ; i < [temp count]; i++) {
               id copied = [copy([temp objectAtIndex:i]) autorelease];
               [temp replaceObjectAtIndex:i withObject:copied];
            }
        } else if ([temp isKindOfClass:[NSDictionary class]]) {
            NSEnumerator *enumerator = [temp keyEnumerator];
            NSString *nextKey;
            while (nextKey = [enumerator nextObject])
                [temp setObject:[copy([temp objectForKey:nextKey]) autorelease]
                         forKey:nextKey];
        } 
        return temp;
    }

    return (copy(self));
}

@end

Затем вы можете вызвать deepMutableCopy следующим образом:

NSMutableDictionary *someDictionary = [someDict deepMutableCopy];
[someDictionary addEntriesFromDictionary:otherDictionary];

Ответ 3

Я добавил это в код, упомянутый выше. Это может быть не совсем правильно, но он обрабатывает случай, когда 2 dict имеет элемент, который не имеет 1 dict.

+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
    NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];
    NSMutableDictionary * resultTemp = [NSMutableDictionary dictionaryWithDictionary:dict1];

    [resultTemp addEntriesFromDictionary:dict2];

    [resultTemp enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
    if ([dict1 objectForKey:key]) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
            [result setObject: newVal forKey: key];
        } else {
            [result setObject: obj forKey: key];
        }
    }
    else if([dict2 objectForKey:key])
    {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            NSDictionary * newVal = [[dict2 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
            [result setObject: newVal forKey: key];
        } else {
            [result setObject: obj forKey: key];
        }
    }
}];

return (NSDictionary *) [[result mutableCopy] autorelease];

}

Ответ 4

Я пришел сюда, чтобы найти копию jQuery extend, но в итоге я написал свою собственную реализацию. Это супер простая реализация, я сделал это, чтобы понять, как это сделать.

+(NSDictionary*) dictionaryByExtending:(NSDictionary*)baseDictionary WithDictionary:(NSDictionary*)extensionDictionary {

    NSMutableDictionary * resultDictionary = [NSMutableDictionary dictionaryWithDictionary:baseDictionary];

    [extensionDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

        BOOL isDict = [obj isKindOfClass:[NSDictionary class]];
        BOOL hasValue = [baseDictionary hasObjectForKey:key] != nil;

        id setObj = obj;

        if( hasValue && isDict ) {

            BOOL hasDict = [[baseDictionary objectForKey:key] isKindOfClass:[NSDictionary class]];

            if( hasDict ) {

                NSDictionary * extendedChildDictionary = [NSDictionary dictionaryByExtending:[baseDictionary objectForKey:key] WithDictionary:obj];
                setObj = extendedChildDictionary;

            }

        }

        [resultDictionary setObject:setObj forKey:key];

    }];

    return resultDictionary;

}

-(NSDictionary*) dictionaryByExtendingWithDictionary:(NSDictionary*)extensionDictionary {
    return [NSDictionary dictionaryByExtending:self WithDictionary:extensionDictionary];
}

Надеюсь, кто-то найдет это полезным, он работал в моих тестах с глубокой рекурсией. Я использую его для расширения файлов JSON с полным текстом.

Ответ 5

Я знаю, что это старый вопрос, но мне нужно сделать то же самое: рекурсивно объединить два словарных объекта. Мне нужно сделать еще один шаг и объединить любые объекты, которые могут быть объединены рекурсивно (конечная цель - слияние двух словарей, созданных из plists). Я принимаю решение на https://github.com/bumboarder6/NSDictionary-merge

Я все еще работаю над проектом, но на момент написания он уже работает (в ограниченном тестировании) для рекурсивного слияния словарей. Массивы и наборы скоро появятся.

Я заметил несколько логических ошибок в некоторых других решениях, которые я видел для этой проблемы, и я, надеюсь, избегал этих ловушек, но критические замечания приветствуются.

Использование прост:

#import "NSMutableDictionary-merge.h"

NSMutableDictionary* dict1 = [NSMutableDictionary ...];
NSDictionary* dict2 = [NSDictionary ...];

[dict1 mergeWithDictionary:dict2];

Ответ 6

#import "NSDictionary+Merge.h"

@implementation NSDictionary (Merge)

+ (NSDictionary *)dictionaryByMerging:(NSDictionary *)src with:(NSDictionary *)new
{
    NSMutableDictionary *result = [src mutableCopy];
    [new enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        if ([obj isKindOfClass:[NSDictionary class]]
            && [src[key] isKindOfClass:[NSDictionary class]]) {

            result[key] = [src[key] dictionaryByMergingWith:obj];
        } else {
            result[key] = obj;
        }
    }];
    return [NSDictionary dictionaryWithDictionary:result];
}

- (NSDictionary *)dictionaryByMergingWith:(NSDictionary *)dict {
    return [[self class] dictionaryByMerging:self with:dict];
}

@end

Ответ 7

Alexsander Akers работает для меня, кроме случая, когда dict2 содержит словарь, который отсутствует в dict1 - он сбой. Я изменил логику:

+ (NSDictionary *) dictionaryByMerging: (NSDictionary *) dict1 with: (NSDictionary *) dict2 {
    NSMutableDictionary * result = [NSMutableDictionary dictionaryWithDictionary:dict1];

    [dict2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
        if (![dict1 objectForKey:key]) {
            [result setObject: obj forKey: key];
        } else if ([obj isKindOfClass:[NSDictionary class]]) {
            NSDictionary * newVal = [[dict1 objectForKey: key] dictionaryByMergingWith: (NSDictionary *) obj];
            [result setObject: newVal forKey: key];
        }
    }];

    return (NSDictionary *) [result mutableCopy];
}