Muszę połączyć dwa NSDictionary
s w jeden pod warunkiem, że jeśli słowniki są w słownikach, są one również połączone.Głęboko połącz NSDictionaries
Mniej więcej tak samo jak funkcja jQuery's extend
.
Muszę połączyć dwa NSDictionary
s w jeden pod warunkiem, że jeśli słowniki są w słownikach, są one również połączone.Głęboko połącz NSDictionaries
Mniej więcej tak samo jak funkcja jQuery's extend
.
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
myślę, że to jest to, czego szukasz:
Po pierwsze, trzeba dokonać głęboki zmienny kopię, więc można utworzyć kategorię na NSDictionary
to zrobić:
@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
Następnie można nazwać deepMutableCopy
takiego:
NSMutableDictionary *someDictionary = [someDict deepMutableCopy];
[someDictionary addEntriesFromDictionary:otherDictionary];
A to jest rekurencyjne? W tym NSDictionaries w NSDictionaries będą również połączone? –
Myślę, że powinniśmy również zastąpić "temp powrotu" z "return [temp autorelease];" i zastąp "return (copy (self))" z "return [(copy (self)) retain];" aby uniknąć przecieków – AmineG
@ someone0 '-mutableCopy' ma na celu zwrócenie własności z powrotem do adresata. W związku z tym nie zwracamy automatycznie wartości zwracanej. –
I dodaje to do kodu wspomnianego powyżej. Może nie być w pełni poprawny, ale obsługuje przypadek, w którym 2 dyktuje element, którego nie ma dyktator.
+ (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];
}
wiem, że to jest stare pytanie, ale muszę zrobić to samo: rekurencyjnie scalić dwa słownika obiektów. Muszę pójść o krok dalej i scalić dowolne obiekty, które mogą być scalone rekurencyjnie (celem końcowym jest połączenie dwóch słowników utworzonych z plists). Hostuję moje rozwiązanie pod https://github.com/bumboarder6/NSDictionary-merge
Nadal pracuję nad projektem, ale od tego pisania już działa (w ograniczonym testowaniu) do rekurencyjnego łączenia słownika. Arrays i zestawy już wkrótce.
Zauważyłem kilka błędów logicznych w niektórych innych rozwiązaniach, które widziałem w odniesieniu do tego problemu, i mam nadzieję, że uniknąłem tych pułapek, ale mile widziana jest krytyka.
użycia jest prosty:
#import "NSMutableDictionary-merge.h"
NSMutableDictionary* dict1 = [NSMutableDictionary ...];
NSDictionary* dict2 = [NSDictionary ...];
[dict1 mergeWithDictionary:dict2];
Przepraszam, zostałem przerwany podczas czytania i, jak się wydaje, nie zrobiłem faktycznie kończy się przed komentowaniem. – griotspeak
Bez obaw. Zamierzam iść dalej i usunąć tę małą interakcję - niezupełnie trafną na końcu :) – Mathew
Przyjechałem tu w poszukiwaniu kopii jQuery extend
ale skończyło się na piśmie własną implementację. To bardzo prosta implementacja, zrobiłem to, więc zrozumiałem sposób na zrobienie tego.
+(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];
}
Mam nadzieję, że ktoś znajdzie to pomocne, zadziałało w moich testach z głęboką rekurencją. Używam go do rozszerzania głębokich plików JSON pełnych tekstu.
Niewielka zmiana: 'BOOL hasValue = [baseDictionary objectForKey: key]! = Nil;' W przeciwnym razie działa dobrze. – Rayfleck
@Rayfleck Dokonałem tej aktualizacji - dziękuję –
#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
Alexsander Akers pracuje dla mnie z wyjątkiem przypadku, gdy dict2 zawiera słownik, którego brakuje w dict1 - ulega awarii. Zmieniłem logikę na:
+ (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];
}
Tak więc, jeśli czytam to poprawnie, logika w tym bloku wyliczania to "Jeśli' dict1' nie zawiera obiektu dla 'key' a obiekt' dict2' dla 'key' jest słownikiem, a następnie scal" dict1 " '' 'obiekt dla' klucza' do obiektu 'dict2' dla' klucza' "... ale wiemy już, że' dict1' nie ma obiektu dla tego 'klucza'. A więc wszystko, co robi blok, to dodawanie dowolnych obiektów z 'dict2', które nie są jeszcze w' dict1' do 'dict1'. – Mathew
Myślę, że twoje pierwsze sprawdzenie w wyliczeniu jest nieprawidłowe i powinno zostać usunięte. 'if (! [dict1 objectForKey: key])' sprawia, że wszystkie klucze, które są w 'dict2', ale nie w dict1, nie zostaną zapisane. Być może jest to zgodne z projektem, ale jeśli chcesz dodać unikalne klucze z 'dict2', nie powinieneś uwzględniać tego czeku. – atreat
Dzięki za to, niezupełnie działałam - dokonałem niewielkiej zmiany - zobacz http://stackoverflow.com/a/43172809/3346628 – pomo