使用NSPredicate筛选大型NSArray

我有一个数组包含170kstring(字典中的单词)和一个string,看起来像“glapplega”。 我试图从string中提取单词“apple”(“apple”是数组中的单词)。 我还需要确保提取的单词至less有3个字符。 我现在的代码如下:

NSPredicate *wordPredicate = [NSPredicate predicateWithFormat:@"'%@' contains[cd] SELF", string]; NSPredicate *lengthPredicate = [NSPredicate predicateWithFormat:@"SELF.length > 2"]; NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[wordPredicate, lengthPredicate]]; return [_words filteredArrayUsingPredicate:lengthPredicate]; 

长度谓词在它自己的工作,但单词谓词不(它返回一个空的数组,尽pipe“苹果”是数组中的单词)。

我怀疑使用SELF作为谓词中的正确expression式可能存在问题,因为我发现所有示例都将它作为左expression式,尽pipe我无法确认这一点。

编辑 :我知道,这可能可以用正则expression式来完成(如这里所述),但希望有一种解决方法,因为正则expression式可以用这么大的数据集缓慢。

如果使用块谓词自己迭代数组,则解决此问题很简单。 在某种程度上,格式化的NSPredicate将不得不归结为此,所以不应该有太大的性能影响。 -[NSString rangeOfString:]可用于testing包含string。

 return [_words filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL (id evaluatedString, NSDictionary *bindings) { return string.length > 2 && [string rangeOfString:evaluatedString].location != NSNotFound; }]]; 

你知道你上面的假设和谓词是完全有效的。 你做错的唯一的事就是引用。 重新格式化你的谓词并使之成为这样,

  NSArray * array = @[@"Apple", @"lega", @"foo", @"bar"]; NSString *string = @"glapplega"; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ contains[cd] SELF and SELF.length > 2", string]; NSLog(@"%@",[array filteredArrayUsingPredicate:predicate]); ( Apple, lega ) 

当您指定格式并向格式提供string时,谓词将自行放置引号。 所以,你一直在这里错过。

 #define rchar (rand() % ('z'-'a') + 'a') - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSMutableArray * mar = [NSMutableArray new]; for (int i = 0; i<170000; i++) { NSString * str = [NSString stringWithFormat:@"%c%c%c%c",rchar, rchar, rchar, rchar]; [mar addObject:str]; } NSString * bigStr = @"asdfghjkl;loiuytrdcvcdrtgvfrtghvcftyghvfghcfdtyjghvncdfjtygmvcnfhjghjkgfhdgsxgrecrvtbkunhlmnhubkujvytchrtxgrecdjvbyhnkbjgcfhvyjhbghnkbjchgdfvbghnukbytvjycterwxrzewxcevfbjnkmjohgytreytwexkutckhtdtcfhvjgkjmhgcjhewwzsserdp9dlkuydssqwsxdchvggjhmgbj"; NSDate *start = [NSDate date]; NSArray * marFiltered = [mar filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { return [bigStr rangeOfString:evaluatedObject].length>2; }]]; NSLog(@"found %lu items in %f seconds", (unsigned long)[marFiltered count], -[start timeIntervalSinceNow]); } 

输出:

 2014-05-11 09:09:53.048 170k[89396:303] found 85 items in 0.542431 seconds 

你可以尝试两个选项来定义谓词。 格式string和块。 以下是一些演示两者的代码。 我已经玩了两个,可以分享,performance是一样的。 我只有耐心运行它的最大值INT32_MAX / 2(很多项目)。

开始。 希望这个澄清和帮助:

  NSString* searchString = @"AB0"; NSUInteger capacity = 1000000; NSMutableArray* array = [NSMutableArray array]; NSLog(@"Fillling array with %lu UUIDS. Be patient.", (unsigned long)capacity); NSUInteger batch = 0; for ( NSUInteger i = 0; i < capacity; i++ ) { [array setObject:[[NSUUID UUID] UUIDString] atIndexedSubscript:i]; if (i != 0 && i % (capacity / 10) == 0 ) { NSLog(@"Completed %lu%%", (unsigned long)++batch * 10); } } NSLog(@"Done."); NSPredicate* formatPredicate = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@ AND SELF.length > 3", searchString]; NSLog(@"Filtering with predicate: %@", formatPredicate); NSArray* formatArray = [array filteredArrayUsingPredicate:formatPredicate]; NSLog(@"Got %lu results.", formatArray.count); NSPredicate* blockPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { NSString* theString = evaluatedObject; return theString.length > 3 && [theString rangeOfString:searchString].location != NSNotFound; }]; NSLog(@"Filtering with predicate: %@", blockPredicate); NSArray* blockArray = [array filteredArrayUsingPredicate:blockPredicate]; NSLog(@"Got %lu results.", blockArray.count); 

PS:我不会在手机上运行这个,如果你正在使用大数字行INT32_MAX 🙂