我已经检查了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];
}
答案 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];
}
}
当然你也应该重新加载表格数据,但我使用了一种自定义方法,我遗漏了。