使用NSPredicates过滤NSMutableArray

时间:2013-02-27 08:49:11

标签: ios objective-c filter nsmutablearray nspredicate

我已经检查了StackOverflow上有关NSPredicates的一些主题,尽管它们都指向了正确的方向,但我必须遗漏一些必要的东西。

我有一个包含产品列表的NSMutableArray。 每种产品都有几个属性,如品牌,类别和类型。

现在我希望我的用户能够使用NSPredicates过滤NSMutableArray,因为如果任何选定的过滤器为空,则不应使用该过滤器。

但是,反过来,如果例如所有过滤器都打开:使用品牌A过滤类别B和类型C,它应该只显示带有猫B和类型C的品牌A.

如果我取消选择Cat B,它将使用C型过滤品牌A.

我已经编写了一些代码,但它主要返回一个空的NSMutableArray,所以我猜我的NSPredicates是关闭的。

我还发现在运行谓词之前我需要默认使用'all products'NSMutableArray,或者在选择新的过滤器选项时它会过滤已经过滤的数组。我应该使用多个阵列和一些BOOLean magick,还是这个问题可以使用NSPredicates来解决?

这是我的代码:

-(void)filterTable
{
    NSPredicate *brandPredicate;
    NSPredicate *categoryPredicate;
    NSMutableArray *compoundPredicateArray;

    if( ![self.selectedBrand isEqual: @"Show All Brands"] || !(self.currentBrand == NULL))
    {
    brandPredicate = [NSPredicate predicateWithFormat:@"brand CONTAINS[cd] %@",self.currentBrand];
    compoundPredicateArray = [ NSMutableArray arrayWithObject: brandPredicate ];
    }

    if( ![self.currentCategory isEqual: @"Show All Categories"] || !(self.currentCategory == NULL)) 
    {
        categoryPredicate = [NSPredicate predicateWithFormat:@"category CONTAINS[cd] %@",self.currentCategory];
        [ compoundPredicateArray addObject: categoryPredicate];
    }

    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
                              compoundPredicateArray ];

    [self.tableData filterUsingPredicate:predicate];
    [self.popNTwinBee dismissPopoverAnimated:YES]; // PopoverController
    [self.tableView reloadData];
}

2 个答案:

答案 0 :(得分:4)

您的代码中存在一些概念错误。

首先,在声明谓词时应该初始化NSMutableArray:

NSMutableArray *compoundPredicateArray = [NSMutableArray array];

现在你只在第一个if()内实例化它,这样如果没有设置品牌过滤器,那么可变数组甚至不会被实例化,因此稍后会向它添加对象(例如在第二次过滤中{ {1}})无效且复合谓词创建为空。 在您的第一个if()内,您将拥有:

if()

您的第二个问题是,正如您正确想象的那样,当您使用[compoundPredicateArray addObject:brandPredicate]; 时,您正在过滤先前已经过滤的内容。

您应该做的是始终将未经过滤的数据保存在filterUsingPredicate中并使用NSArray方法检索您将用于显示数据的新过滤的filteredArrayUsingPredicate

答案 1 :(得分:1)

我好好看了一下代码并想出了这个:

this site获得了这个方便的小代码块。

<强>的NSArray + filter.h

#import <Foundation/Foundation.h>

@interface NSArray (Filter)
- (NSArray*)filter:(BOOL(^)(id elt))filterBlock;
@end

<强>的NSArray + filter.m

#import "NSArray+filter.h"

@implementation NSArray(Filter)
- (NSArray*)filter:(BOOL(^)(id elt))filterBlock
{ // Create a new array
    id filteredArray = [NSMutableArray array]; // Collect elements matching the block condition
    for (id elt in self)
        if (filterBlock(elt))
        [filteredArray addObject:elt];
    return  filteredArray;
}
@end

并相应地编辑了我的方法。

TableViewController.m

- (void)filterTable {
    WebServiceStore *wss = [WebServiceStore sharedWebServiceStore];
    self.allProducts = [wss.allProductArray mutableCopy];
    NSArray *filteredOnBrand;
    NSArray *filteredOnCategory;
    NSArray *filteredOnCategoryAndBrand;

    if (![self.currentBrand isEqualToString:@"All Brands"] && !(self.currentBrand == nil)) 
    {
        filteredOnBrand = [self.allProducts filter:^(id elt) 
        {
            return [[elt brand] isEqualToString:self.currentBrand];
        }];
        [self.tableData removeAllObjects];
        [self.tableData addObjectsFromArray:filteredOnBrand];
    }

    if ([self.currentBrand isEqualToString:@"All Brands"] || self.currentBrand == nil) 
    {
        filteredOnBrand = [self.allProducts mutableCopy];
        [self.tableData removeAllObjects];
        [self.tableData addObjectsFromArray:filteredOnBrand];
    }

    if (![self.currentCategory isEqualToString:@"All Categories"] && !(self.currentCategory == nil)) 
    {
        filteredOnCategory = [self.allProducts filter:^(id elt) 
      {
            return [[elt category] isEqualToString:self.currentCategory];
      }];
        [self.tableData removeAllObjects];
        [self.tableData addObjectsFromArray:filteredOnCategory];
    }
    if (![self.currentCategory isEqualToString:@"All Categories"] && !(self.currentCategory == nil) && ![self.currentBrand isEqualToString:@"All Brands"] && !(self.currentBrand == nil)) {
        filteredOnBrand = [self.allProducts filter:^(id elt) {
            return [[elt brand] isEqualToString:self.currentBrand];
        }];
        filteredOnCategoryAndBrand = [filteredOnBrand filter:^(id elt) {
            return [[elt category] isEqualToString:self.currentCategory];
        }];
        [self.tableData removeAllObjects];
        [self.tableData addObjectsFromArray:filteredOnCategoryAndBrand];
    }
}

当然你也应该重新加载表格数据,但我使用了一种自定义方法,我遗漏了。