3

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

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

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

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

如果我随后取消选择 Cat B,它将使用 C 类过滤品牌 A。

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

我还发现在运行谓词之前我需要默认为“所有产品”NSMutableArray,否则在选择新的过滤器选项时它会过滤已经过滤的数组。我应该使用带有一些布尔魔法的多个数组,还是可以使用 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];
}
4

2 回答 2

4

您的代码中有几个概念性错误。

首先,您应该在声明 NSMutableArray 谓词时对其进行初始化:

NSMutableArray *compoundPredicateArray = [NSMutableArray array];

现在你只在你的 first 中实例化它if(),所以如果没有设置品牌过滤器,可变数组甚至不会被实例化,所以稍后添加对象(例如在第二个过滤中if())是无效的并且复合谓词创建为空. 在您的第一个内部if(),您将拥有:

[compoundPredicateArray addObject:brandPredicate];

您的第二个问题是,正如您正确想象的那样,当您使用filterUsingPredicate.

您应该做的是始终将未过滤的数据保留在 a 中NSArray,并使用其filteredArrayUsingPredicate上的方法来检索NSArray您将用于显示数据的新过滤器。

于 2013-02-27T09:31:13.503 回答
1

好吧,我仔细查看了代码并想出了这个:

从这个站点得到了这个方便的小代码块。

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];
    }
}

当然,您还应该在之后重新加载表数据,但我为此使用了自定义方法,但我忽略了。

于 2013-03-06T09:02:21.557 回答