递归遍历未知结构的NSDictionary

Posted

技术标签:

【中文标题】递归遍历未知结构的NSDictionary【英文标题】:Recursively traverse NSDictionary of unknown structure 【发布时间】:2012-02-29 11:11:15 【问题描述】:

有没有人对未知结构的 NSDictionary 进行递归有序遍历?我想采用任何 NSDictionary 并按层次顺序处理每个级别。

1) 此数据来自经过验证的 JSON。是否可以肯定地说,从 SBJSON(JSON 框架)等框架创建的 NSDictionary 只会产生嵌套字典、数组和任意叶子的组合?

2) 如何使用适用于数组和字典的快速枚举来完成通用遍历?使用下面的代码,一旦我到达数组中的字典,它就会停止遍历。但是,如果我在数组条件中继续递归(以检查数组中的字典),它会在id value = [dict valueForKey:key]; 的下一次迭代中使用-[__NSCFDictionary length]: unrecognized selector sent to instance SIGABRT。我不知道为什么这会是个问题,因为我已经通过***字典(在其中找到子级字典数组)超过了那条线。

-(void)processParsedObject:(id)dict counter:(int)i parent:(NSString *)parent

    for (id key in dict) 
        id value = [dict valueForKey:key];
        NSLog(@"%i : %@ : %@ -> %@", i, [value class], parent, key);

        if ([value isKindOfClass:[NSDictionary class]])
        
            i++;
            NSDictionary* newDict = (NSDictionary*)value;
            [self processParsedObject:newDict counter:i parent:(NSString*)key];
            i--;
        
        else if ([value isKindOfClass:[NSArray class]])
        
            for (id obj in value) 
                NSLog(@"Obj Type: %@", [obj class]);
            
        
    

非常感谢

【问题讨论】:

你每次 NSLog 一个 NSDictionary 时都这样做。 对,但总的来说,重点是在进程中捕获和处理嵌套的对象,而不仅仅是记录它们。 你只需要学习如何使用isKindOfClass 【参考方案1】:

我做过类似的事情,我会遍历来自 Web 服务的 JSON 结构化对象并将每个元素转换为可变版本。

- (void)processParsedObject:(id)object

    [self processParsedObject:object depth:0 parent:nil];


- (void)processParsedObject:(id)object depth:(int)depth parent:(id)parent
      
    if ([object isKindOfClass:[NSDictionary class]])
    
        for (NSString* key in [object allKeys])
        
            id child = [object objectForKey:key];
            [self processParsedObject:child depth:(depth + 1) parent:object];
        
    
    else if ([object isKindOfClass:[NSArray class]])
    
        for (id child in object)
        
            [self processParsedObject:child depth:(depth + 1) parent:object];
           
    
    else
    
        // This object is not a container you might be interested in it's value
        NSLog(@"Node: %@  depth: %d", [object description], depth);
    

【讨论】:

优秀。我会试一试并报告。 像魅力一样工作!我喜欢您将其分解为单参数方法的方式,因此调用者不需要知道晦涩的参数。 为什么传递parent?好像从来没用过。 @AaronBrager 的确如此,以及为什么 'depth? And, it's better to iterate [object allObjects]` 而不是间接通过 allKeys【参考方案2】:

我需要知道值的键名,所以我重写了 Joel 的内容并想出了这个:

- (void)enumerateJSONToFindKeys:(id)object forKeyNamed:(NSString *)keyName

    if ([object isKindOfClass:[NSDictionary class]])
    
        // If it's a dictionary, enumerate it and pass in each key value to check
        [object enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) 
            [self enumerateJSONToFindKeys:value forKeyNamed:key];
        ];
    
    else if ([object isKindOfClass:[NSArray class]])
    
        // If it's an array, pass in the objects of the array to check
        [object enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) 
            [self enumerateJSONToFindKeys:obj forKeyNamed:nil];
        ];
    
    else
    
        // If we got here (i.e. it's not a dictionary or array) so its a key/value that we needed 
        NSLog(@"We found key %@ with value %@", keyName, object);
    

然后你会这样称呼它:

[self enumerateJSONToFindKeys:JSON forKeyNamed:nil];

或者,如果您想让给定的键路径可变(这样您可以使用setValue:forKeyPath: 回写它),您可以执行以下操作:

- (void)makeDictionariesMutableForKeyPath:(NSString *)keyPath 
    NSArray *keys = [keyPath componentsSeparatedByString:@"."];

    NSString *currentKeyPath = nil;
    for (NSString *key in keys) 
        if (currentKeyPath) 
            NSString *nextKeyPathAddition = [NSString stringWithFormat:@".%@", key];
            currentKeyPath = [currentKeyPath stringByAppendingString:nextKeyPathAddition];
         else 
            currentKeyPath = key;
        

        id value = [self.mutableDictionary valueForKeyPath:currentKeyPath];
        if ([value isKindOfClass:NSDictionary.class]) 
            NSMutableDictionary *mutableCopy = [(NSDictionary *)value mutableCopy];
            [self.mutableDictionary setValue:mutableCopy forKeyPath:currentKeyPath];
        
    


【讨论】:

嗨,有没有办法返回所有的ketPath? ...类似于“keyname.keyname.keyname”的所有子键??

以上是关于递归遍历未知结构的NSDictionary的主要内容,如果未能解决你的问题,请参考以下文章

《数据结构》遍历二叉树的非递归算法的疑问。

数据结构——二叉树遍历之“递归与非递归遍历”

python 递归遍历目录下的文件,以处理和映射到相同的目录结构。递归遍历文件,获取相同目录结构的目标路径。

Java数据结构——二叉树的递归与非递归遍历(DFS)

C语言数据结构,急求在线二叉树先序中序后序递归遍历

数据结构 - 二叉树遍历(递归,非递归)与构造