NSPredicate而不是循环来过滤对象数组

时间:2012-07-11 18:55:21

标签: objective-c nspredicate

我被告知我可以使用NSPredicate复制此方法的结果

- (void) clearArrayOut
{
    bool goAgain = false;

    for (int j=0; j<[array count]; j++)
    {
        if ([[array objectAtIndex:j] someMethod] == NO)
        {
            [array removeObjectAtIndex:j];
            goAgain = true;
            break;
        }
    }

    if (goAgain) [self clearArrayOut];
}

如何根据自定义类调用的某些方法的结果制作一个过滤数组的NSPredicate

3 个答案:

答案 0 :(得分:10)

使用应用过滤器制作副本:

NSArray *filteredArray = [someArray filteredArrayUsingPredicate:
    [NSPredicate predicateWithBlock:^(id object, NSDictionary *bindings) {
        return [object someMethod]; // if someMethod returns YES, the object is kept
    }]];

过滤NSMutableArray到位:

[someMutableArray filterUsingPredicate:
    [NSPredicate predicateWithBlock:^(id object, NSDictionary *bindings) {
        return [object someMethod]; // if someMethod returns YES, the object is kept
    }]];

但如果我过滤一个小数组,我可能只会使用for循环。但是,我会以不同的方式编写for循环,以避免不得不递减索引变量或递归调用自己:

- (void)clearArrayOut:(NSMutableArray *)array {
    for (int i = array.count - 1; i >= 0; --i) {
        if (![[array objectAtIndex:i] someMethod]) {
            [array removeObjectAtIndex:i];
        }
    }
}

答案 1 :(得分:1)

您只需将其写入谓词中,例如,假设您有一个名为isOdd的方法的对象,并且您希望过滤您的数组以仅包含对isOdd返回true的对象,你可以这样做:

#import <Foundation/Foundation.h>

@interface barfoo : NSObject 
{
    int number;
}

- (BOOL)isOdd;
- (id)initWithNumber:(int)number;

@end

@implementation barfoo

- (NSString *)description
{
    return [NSString stringWithFormat:@"%i", number];
}

- (BOOL)isOdd
{
    return (number % 2);
}
- (id)initWithNumber:(int)tnumber
{
    if((self = [super init]))
    {
        number = tnumber;
    }

    return self;
}

@end


int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray array];
        for(int i=0; i<10; i++)
        {
            barfoo *foo = [[barfoo alloc] initWithNumber:i];
            [array addObject:[foo autorelease]];
        }

        NSLog(@"%@", array); // prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isOdd == true"]; // This is oure predicate. isOdd must be true for objects to pass
        NSArray *result = [array filteredArrayUsingPredicate:predicate];

        NSLog(@"%@", result);
    }
}

当然这也可以反过来,你的谓词也可以读isOdd == false,或者你可以为要传递的对象添加更多的要求。例如isOdd == true AND foo == bar。您可以在NSPredicate文档中了解有关NSPredicate语法的更多信息。

答案 2 :(得分:1)

您的实现开始时非常低效:不是递归地继续删除,如果对象已被删除,您可以将循环更改为不进行,如下所示:

- (void) clearArrayOut {
    int j = 0;
    while (j < [array count]) {
        if ([[array objectAtIndex:j] someMethod] == NO) {
            [array removeObjectAtIndex:j];
        } else {
            j++;
        }
    }
}

您可以使用filterUsingPredicate:执行相同操作,如下所示:

- (void) clearArrayOut {
    NSPredicate *p = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
        return [obj someMethod] == NO
    }];
    [array filterUsingPredicate:p];
}