2

次の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++

ありがとう。

4

3 に答える 3

1

@Hotが述べているように:

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

レベルを最後までスローしてからカウントを使用するだけのオーバーヘッドは、実際にはそれほど多くありません。

于 2013-01-05T23:07:13.797 に答える
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");
}
于 2014-07-23T20:31:48.460 に答える
0

1 つのアプローチは、再帰を使用することです。このアプローチは最も安価ではありませんが、構造の深さが変更されたり、コンパイル時に不明な場合に柔軟に対応できるという利点があります。

擬似コード:

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パラメーターにより、必要以上の作業が行われないことが保証されます (@RamyAlZuhouri がコメントで提案したように、配列を反復処理して反復ごとにカウントを 1 ずつ増やすのではなく、単に maxDepth のときに配列のカウントを返すだけです)。

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」)")。

于 2013-01-06T04:44:10.097 に答える