NSDictionary统计所有孩子

时间:2013-01-05 21:22:34

标签: objective-c cocoa

我有以下 NSDictionary 结构:

// level
0    1    2    3

// elements
a1 - b1 - c1 - d111.1
             - d111.2
             - d111.3
          c2 - d112.1
             - d112.2
          c3 - d113.1

考虑到数据文件将是圆形的2000-5000元素。什么是在3级(a1 = 0)计算子项的快速方法?要明确:我想要包含在计数中:a1,b1,c1-c3,只有d *。

你能用NSPredicate做些什么吗?

还是老式的(但价格昂贵的)for-loop方法(eeks)?

// pseudo-code
count=0
for (a in root)
 for (b in a)
   for (c in b)
     for (d in c)
       count++

感谢。

3 个答案:

答案 0 :(得分:1)

正如@Hot所说:

int count = 0;
   for (NSArray *a in root)
      for (NSArray *b in a)
         for (NSArray *c in b)
            count += c.count;

实际上没有太多的开销可以直接通过最后一级,然后只使用计数。

答案 1 :(得分:1)

使用类别的解决方案。

@implementation NSDictionary (RecursiveCount)
- (NSUInteger)recursiveCount {
    return self.allValues.recursiveCount;
}
@end

@implementation NSArray (RecursiveCount)
- (NSUInteger)recursiveCount {
    int count = 0;
    for (id object in self) {
        if ([object isKindOfClass:[NSDictionary class]] || [object isKindOfClass:[NSArray class]]) {
            count += [object recursiveCount];
        } else {
            ++count;
        }
    }
    return count;
}
@end

- (void)testRecursiveCount {
    NSDictionary *dict = @{
                           @"key1" : @[@1, @2, @3],
                           @"key2" : @{@"blah" : @[@[], @1, @[@1, @2]]},
                           @"key3" : @4,
                           @"key5" : @[@{@"blah":@[@{@1:@1}]}],
                           };
    XCTAssertEqual(dict.recursiveCount, 8, @"dictionary");

    NSArray *array = @[
                       @[@1, @2, @3],
                       @{@"blah" : @[@[], @1, @[@1, @2]]},
                       @4,
                       @[@{@"blah":@[@{@1:@1}]}],
                       ];
    XCTAssertEqual(array.recursiveCount, 8, @"dictionary");
}

答案 2 :(得分:0)

一种方法是使用递归。这种方法并不是最便宜的,但它确实具有灵活性的优势 - 如果结构深度发生变化或在编译时未知。

在伪代码中:

integer count ( collection , maxDepth )
    return recursiveCount ( collection , 0 , maxDepth )
end

integer recursiveCount ( collection , currentDepth , maxDepth )
    integer count = 0
    if collection is array
        count += recursiveCountArray ( collection , currentDepth , maxDepth )
    else if object is dictionary
        count += recursiveCountDictionary ( collection.values , currentDepth , maxDepth )
    else
        count += 1
    return count
end

integer recursiveCountArray ( array , currentDepth , maxDepth )
    integer count = 0
    if currentDepth < maxDepth
        for ( object in array )
            count += recursiveCount( object )
    else
        count += array.count
    return count
end

maxDepth参数确保不再需要完成工作(只需在maxDepth处返回数组的计数,而不是遍历数组,并在每次迭代时将计数递增1为@RamyAlZuhouri在他的评论中提出。)

在Objective-C中,这可以通过C函数来实现:

static NSUInteger _PFXRecursiveCount(id collection, NSUInteger currentDepth, NSUInteger maxDepth);
static NSUInteger _PFXRecursiveCountArray(id array, NSUInteger currentDepth, NSUInteger maxDepth);

NSUInteger PFXRecursiveCount(id collection, NSUInteger maxDepth)
{
    return _PFXRecursiveCount(collection, 0, maxDepth);
}

NSUInteger _PFXRecursiveCount(id collection, NSUInteger currentDepth, NSUInteger maxDepth)
{
    NSUInteger count = 0;
    if ([collection isKindOfClass:[NSArray class]]) {
        count = _PFXRecursiveCountArray(collection, currentDepth, maxDepth);
    } else if ([collection isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = (NSDictionary *)collection;
        count = _PFXRecursiveCountArray(dictionary.allValues, currentDepth, maxDepth);
    } else {
        count = 1;
    }
    return count;
}

NSUInteger _PFXRecursiveCountArray(NSArray *array, NSUInteger currentDepth, NSUInteger maxDepth)
{
    NSUInteger count = 0;
    if (currentDepth < maxDepth) {
        for (id object in array) {
            count += _PFXRecursiveCount(object, currentDepth + 1, maxDepth);
        }
    } else {
        count += array.count;
    }
    return count;
}

此处PFX将替换为项目中使用的相应前缀。只会在标题中声明PFXRecursiveCount

或者,这可以通过块来实现:

typedef NSUInteger (^RecursiveCountArrayBlock)(NSArray *, NSUInteger, NSUInteger);
typedef NSUInteger (^RecursiveCountBlock)(id, NSUInteger, NSUInteger);

__block __weak RecursiveCountArrayBlock _weakRecursiveCountArray = nil;
__block __weak RecursiveCountBlock _weakRecursiveCount = nil;

NSUInteger (^_recursiveCount)(id, NSUInteger, NSUInteger) = ^(id collection, NSUInteger currentDepth, NSUInteger maxDepth) {
    NSUInteger count = 0;
    if ([collection isKindOfClass:[NSArray class]]) {
        count = _weakRecursiveCountArray(collection, currentDepth, maxDepth);
    } else if ([collection isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = (NSDictionary *)collection;
        count = _weakRecursiveCountArray(dictionary.allValues, currentDepth, maxDepth);
    } else {
        count = 1;
    }
    return count;
};
_weakRecursiveCount = _recursiveCount;

NSUInteger (^_recursiveCountArray)(id, NSUInteger, NSUInteger) = ^(NSArray *array, NSUInteger currentDepth, NSUInteger maxDepth) {
    NSUInteger count = 0;
    if (currentDepth < maxDepth) {
        for (id object in array) {
            count += _weakRecursiveCount(object, currentDepth + 1, maxDepth);
        }
    } else {
        count += array.count;
    }
    return count;
};
_weakRecursiveCountArray = _recursiveCountArray;

NSUInteger (^recursiveCount)(id, NSUInteger) = ^(id collection, NSUInteger maxDepth) {
    return _recursiveCount(collection, 0, maxDepth);
};

__weak __block个变量(_weakRecursiveCountArray_weakRecursiveCount)允许我们避免从内部强烈引用该块。 (注意:在iOS 5和10.7之前,__weak需要替换为__unsafe_unretained。)typedef允许我们避免虚假警告(仅限''__weak'适用于objective-c对象或块指针类型;此处键入'NSUInteger'(又名'unsigned long')“)。