2013-01-05 44 views
2

我有以下的NSDictionary結構:的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++ 

謝謝。

+0

這就像一棵樹,你需要一個BFS,你停留在3級。 –

+4

我不明白for循環如何比謂詞更昂貴,它會做同樣的事情,但以一種奇特的方式包裹起來。 – Jack

+2

它可以得到一個簡單的只是總結最裏面的字典'count'值,而不是走他們。 –

回答

1

由於@Hot指出:

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

實在是沒有太大的開銷,只是通過各級猛擊,直至最後,然後只用計數。

+0

如果'root'是陣列數組的陣列,那麼這是有效的。那裏沒有字典。對於提問者的問題,其中至少有一個關卡是字典,你需要在某個時候發送'objectForKey:'(假設所有關鍵字都不可枚舉),或者使用'enumerateKeysAndObjectsUsingBlock:'。 –

+0

是的,問題不明確。 – zaph

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而不是迭代的陣列上方和添加通過遞增計數@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。)typedefs允許我們避免虛假警告(「'__weak'僅適用於objective-c對象或塊指針類型;在此輸入是'NSUInteger'(又名'unsigned long')「)。

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"); 
}