搜索 NSArray 包含 NSDictionary,具有“时间效率”

Posted

技术标签:

【中文标题】搜索 NSArray 包含 NSDictionary,具有“时间效率”【英文标题】:search NSArray contains NSDictionary, with "time efficiency" 【发布时间】:2013-03-07 11:14:12 【问题描述】:

我知道已经有很多问题被问到了,但是我还要再提出一个问题。我有一个数组包含大量(数千条记录)NSDictionary 格式的数据,我正在尝试在字典中的键中执行搜索到数组中。

我在UITextField- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 数据源方法中进行搜索,

我的搜索要求是在整个字符串中,

示例字符串,

aaa, abeb, abcd, abbec 类字符串

搜索流程,

如果 a 将所有字符串返回给我,

如果aa 只返回aaa,

如果ab返回abeb, abcd, abbec喜欢,

重要的是,如果它的 cd 则只返回 abcd

我用这些方法试过了,

使用 NSPredicates

NSLog(@"start search at : %@",[NSDate date]);
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"Name contains[cd] %@", matchString];
searchArray = [[meadArray filteredArrayUsingPredicate:predicate] mutableCopy];
NSLog(@"Search found count = %d",searchArray.count);
[tableCheck reloadData];
NSLog(@"End search at : %@",[NSDate date]);

另一种方式——通过迭代

NSLog(@"start search at : %@",[NSDate date]);
for (NSDictionary *word in arrayNames)

    if ([matchString length] == 0)
    
        [searchArray addObject:word];
        continue;
    
    NSRange lastRange = [[[word valueForKey:@"Name"] uppercaseString] rangeOfString:upString];

    if ( lastRange.location != NSNotFound)
    
        if(range.location == 0 || lastRange.location == 0)
        
            [searchArray addObject:word];
        
    
    
NSLog(@"End search at : %@",[NSDate date]);

这两种方法都运行良好,结果符合我的预期,但仅在模拟器中!当我在设备中测试相同的内容时,根据搜索的扩展,它大约需要 1 / 2 / 3 秒,首先说如果我输入,a 需要 3 秒,aa 需要大约 2 秒,所以在。 IT LOOKS CLUMSY ON DEVICE, ANY PRESSED KEY WILL BE REMAIN HIGHLIGHTED UNTIL SEARCH NOT DONE.

有什么方法可以让我使用我正在使用的相同方法或任何其他替代方法来执行更快的搜索!

更新 1

也尝试使用 CFArrayBSearchValues 它只返回搜索字符串的索引,但我想要返回所有匹配的字符串。

unsigned index = (unsigned)CFArrayBSearchValues((CFArrayRef)meadArray, CFRangeMake(0, CFArrayGetCount((CFArrayRef)meadArray)), (CFStringRef)matchString,(CFComparatorFunction)CFStringCompare, NULL);

更新 2

根据Alladinian 的评论,我在后台线程中执行了搜索操作,是的,现在它没有锁定 UI,但搜索仍然太慢,我正在做的是,执行选择器对于一些延迟说 0.25 秒,还取消任何以前的选择器调用,然后在后台执行搜索,也在主线程中重新加载表。它的工作方式就像,如果我输入字符有一些延迟,它的效果很好,但是如果我一次输入整个单词,它会根据按下的字符加载/更新表,最后它会显示实际输出,需要 3-显示实际内容需要 4 秒。

非常感谢任何建议或帮助!

【问题讨论】:

【参考方案1】:

您的搜索速度比需要的慢。 searchString.length 不应该在循环中检查,应该在外面完成。 valueForKey:是一种非常通用的方法,允许您访问任何类型的不同键路径 - objectForKey 快得多!接下来,您对区分大小写匹配的检查总是将整个单词转换为大写。一旦您的用户输入了一些更有趣的搜索字符串,这不仅是不正确的,而且它也很慢并且占用大量内存(如果有数千个字符串,您将创建数千个自动释放的对象)。使用 rangeOfString:options: 代替。

最后,您可以尝试 NSArray 方法 enumerateObjectsUsingBlock: 或 enumerateObjectsWithOptions:usingBlock: 允许在多个线程上进行枚举。

indexesOfObjectsPassingTest: 或indexesOfObjectsWithOptions:passingTest: 获取你要查找的元素的索引,这样会更快。如果您再次搜索更长的字符串,这尤其有用,例如在搜索 ab 之后搜索 abc,因为有 NSArray 方法将搜索限制为索引集。

【讨论】:

【参考方案2】:

我在几个项目中使用了 sqlite 的Full Text Search modules,它们工作得非常好。这将需要提供您自己编译的 sqlite 版本,这可能有点棘手 - 但整个过程在 here 中进行了解释。

根据您的需要,您可能需要预先填充此数据库,或在收到数据时插入数据 - 或两者兼而有之。尝试自己编写解决方案将是一个有趣的练习,但您将重新发明 sqlite FTS 中已经存在的算法。

如果您在输入时进行搜索,则必须确保您在后台正确地对操作进行排队并且它们是可取消的 - 有很多方法可以实现这一点; a very popular one uses NSOperation.

希望这会有所帮助。

【讨论】:

【参考方案3】:

这个答案是在anktastic 答案之后,我也在添加我的答案,因为有人会直接得到我们付出了很多努力的解决方案:)

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 

    //Cancel any previous selector calls
    [NSRunLoop cancelPreviousPerformRequestsWithTarget:self];

        if(string.length > 0)
        
            NSString *str = [txt1.text substringToIndex:[txt1.text length] - 1];
            //delay is useful in smooth search - if you're performing web service calls for searching on cloud, then you should give delay as per your test
            [self performSelector:@selector(startSearch:) withObject:str afterDelay:0.05f];
        
    


- (void) startSearch:(NSString *)matchString

    //Cancel any previous operation added in queue
    [queue cancelAllOperations];
    //Create new operation
    NSInvocationOperation* operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(search:) object:matchString];
    [queue addOperation:operation];


- (void) search:(NSString *)matchString

    //Performing search operation
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"Name contains[cd] %@", matchString];
    searchArray = [[meadArray filteredArrayUsingPredicate:predicate] mutableCopy];
    //only call again after any previous reload done
    [self performSelectorOnMainThread:@selector(reloadInMainThread) withObject:nil waitUntilDone:YES];


- (void) reloadInMainThread 

    //Reloading table in main thread for instance search effect
    [tableCheck reloadData];

【讨论】:

这在 ios 7 上完美运行,但在 iOS 8 上似乎失败了。 我使用的是默认值 maxConcurrentOperationCount。必须将其设置为 1。[_queue setMaxConcurrentOperationCount:1]; 现在,在 iOS 8 上也可以完美运行。【参考方案4】:

数组/字典中的字符串会改变吗?如果没有,我建议您将字典中的所有键提取到单个数组中。 在搜索之前执行此操作,您只需执行一次。

如果我没有误解你的问题,我的建议是这样的:

NSArray *arrayOfDicts = /*the array with dictionarys you have*/

NSMutableArray *allKeys = [NSMutableArray array]

for (NSDictionary *d in arrayOfDicts) 
     for (NSString *key in d) 
          [allKeys addObject:key];
     

然后搜索 allKeys 数组。

【讨论】:

【参考方案5】:

不区分大小写的字符串比较比区分大小写的比较慢。您可以将 uppercaseName 存储为字典中的附加键(或 sqlite 表中的附加列)并将谓词更改为

[NSPredicate predicateWithFormat:@"uppercaseName CONTAINS %@", [matchString uppercaseString]]

【讨论】:

@jrturton: CONTAINS[c] 指定大小写不敏感(“abc”和“ABC”被视为相同),应该更慢而不是区分大小写的比较。 - 我已回滚您的编辑。如果我完全错了,请告诉我。 对不起,我睡着了。应该知道比怀疑你更好。我读错了你的答案,并认为你的观点不同。

以上是关于搜索 NSArray 包含 NSDictionary,具有“时间效率”的主要内容,如果未能解决你的问题,请参考以下文章

搜索 NSArray 包含 NSDictionary,具有“时间效率”

NSArray 上的 NSPredicate 用于搜索任何对象

检索数据并将 JSON 记录保存在数组中

使用 NSPredicate 搜索 NSArray

在对象的 NSArray 中搜索与任何属性匹配的字符串

使用 NSPredicate 搜索核心数据实体的 NSArray 属性