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

时间:2013-03-07 11:14:13

标签: iphone nsarray nsdictionary nspredicate cfarraybsearchvalues

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

我正在使用UITextField - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 数据源方法

执行搜索

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

示例字符串,

  aaa,abeb,abcd,abbec like strings

搜索流程,

如果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秒钟。

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

5 个答案:

答案 0 :(得分:2)

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

最后,您可以尝试使用NSArray方法enumerateObjectsUsingBlock:或enumerateObjectsWithOptions:usingBlock:它允许枚举在多个线程上发生。

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

答案 1 :(得分:1)

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

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

如果您正在进行搜索,那么您必须确保在后台正确排队操作并且可以取消它们 - 有很多方法可以解决这个问题。 a very popular one uses NSOperation

希望这有帮助。

答案 2 :(得分:1)

这个答案是在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];
}

答案 3 :(得分:0)

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

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

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数组。

答案 4 :(得分:0)

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

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