深度结合 NSDictionaries
Posted
技术标签:
【中文标题】深度结合 NSDictionaries【英文标题】:Deep combine NSDictionaries 【发布时间】:2010-10-25 03:43:44 【问题描述】:我需要将两个NSDictionary
s 合并为一个,前提是如果字典中有字典,它们也会被合并。
或多或少类似于 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
【讨论】:
所以,如果我没看错的话,枚举块中的逻辑是“如果dict1
不包含key
的对象和dict2
的key
对象是一个字典,然后将dict1
的key
对象合并到dict2
的key
对象中……但我们已经知道dict1
没有那个key
的对象。因此,该块所做的就是将 dict2
中尚未在 dict1
中的任何对象添加到 dict1
中。
我认为您在枚举中的第一次检查不正确,应该删除。 if (![dict1 objectForKey:key])
使得任何在 dict2
但不在 dict1 中的键都不会被写入。也许这是设计使然,但如果您想添加来自 dict2
的任何唯一键,则不应包含此检查。【参考方案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];
【讨论】:
这是递归的吗?在那个NSDictionaries中的NSDictionaries也会被合并? 我认为我们也应该替换“return temp;”用“返回[临时自动释放];”并替换“return (copy(self));”与“返回[(复制(自我))保留];”避免泄漏 @someone0-mutableCopy
暗示将所有权归还给被调用者。因此,我们不会自动释放返回值。
@Jacob Relkin 是的,但我认为使用 setObject:forKey 插入复制的对象时存在问题:它将被复制然后保留。在这种情况下,我将为使用 replaceObjectAtIndex: 和 setObject:forKey: 插入的对象添加自动释放
@JacobRelkin 另一个细节:要让编译代码“id copy(id obj)”定义应该在“- (id)deepMutableCopy”实现之外。【参考方案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 文件。
【讨论】:
小改动:BOOL hasValue = [baseDictionary objectForKey:key] != nil;
否则,效果很好。【参考方案5】:
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];
【讨论】:
【参考方案6】:我知道这是一个老问题,但我需要做同样的事情:递归合并两个字典对象。我需要更进一步,合并任何可以递归合并的对象(最终目标是合并从 plists 创建的两个字典)。我将我的解决方案托管在https://github.com/bumboarder6/NSDictionary-merge
我仍在从事该项目,但在撰写本文时,它已经可以用于递归字典合并(在有限的测试中)。数组和集合即将推出。
我注意到在我为这个问题看到的一些其他解决方案中存在一些逻辑错误,我希望能避免这些陷阱,但欢迎批评。
用法很简单:
#import "NSMutableDictionary-merge.h"
NSMutableDictionary* dict1 = [NSMutableDictionary ...];
NSDictionary* dict2 = [NSDictionary ...];
[dict1 mergeWithDictionary:dict2];
【讨论】:
很抱歉,我在阅读时被打断了,看来我还没有真正完成评论。 不用担心。我将继续删除这个小交互——最终并不完全相关:)【参考方案7】:#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
【讨论】:
【参考方案8】:我需要一种方法来递归地合并(附加)两个 JSON 对象中的对象,重点关注其中的 NSDictionaries,同时还要考虑 NSArray,并在此过程中优雅地处理类型不匹配的情况。这里的其他答案并没有那么远,所以我需要自己写。以下处理所有这些情况。因为验证在顶部而不是在中间,所以它可以从混合的非空和可空对象开始使用。将来可能会扩展它以支持可能适用附加的其他类型。要使用,请将 xxx_ 前缀重命名为您自己的小写三位数前缀。这是合适的,因为这是对基础类的扩展:
NSObject+Append.h
@interface NSObject (Append)
+ (nullable id)xxx_objectAppendingObject1:(nullable id)object1 object2:(nullable id)object2 NS_SWIFT_NAME(kva_objectAppending(object1:object2:));
@end
NSObject+Append.m
@implementation NSObject (Append)
+ (nullable id)xxx_objectAppendingObject1:(nullable id)object1 object2:(nullable id)object2
// VALIDATE ELSE RETURN
if (object1 == nil)
return object2;
if (object2 == nil)
return object1;
// MAIN
// dictionary1
NSDictionary *dictionary1 = [object1 isKindOfClass:NSDictionary.class] ? (NSDictionary *)object1 : nil;
// dictionary2
NSDictionary *dictionary2 = [object2 isKindOfClass:NSDictionary.class] ? (NSDictionary *)object2 : nil;
// array1
NSArray *array1 = [object1 isKindOfClass:NSArray.class] ? (NSArray *)object1 : nil;
// array2
NSArray *array2 = [object2 isKindOfClass:NSArray.class] ? (NSArray *)object2 : nil;
// A. NSDICTIONARY TO NSDICTIONARY
if ((dictionary1 != nil) && (dictionary2 != nil))
NSMutableDictionary *returnDictionary = dictionary1.mutableCopy;
[dictionary2 enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop)
returnDictionary[key] = [self.class kva_objectAppendingObject1:dictionary1[key] object2:obj];
];
return returnDictionary;
// B. NSARRAY TO NSARRAY
if ((array1 != nil) && (array2 != nil))
return [array1.mutableCopy arrayByAddingObjectsFromArray:array2];
// DEFAULT
return object2;
@end
【讨论】:
以上是关于深度结合 NSDictionaries的主要内容,如果未能解决你的问题,请参考以下文章