在字典数组中搜索字符串数组

4
我有一个组合的数组需要在另一个字典数组中进行搜索。
字典数组如下:

self.listOfAllContacts

({
    name = "William";
    recordId = 541;
},
{
    name = "Soan";
    recordId = 541;
},
{
    name = "kamal";
    recordId = 541;
},
{
    name = "Elisia";
    recordId = 541;
},  
{
    name = "Ben";
    recordId = 541;
},
{
    name = "Loki";
    recordId = 541;
},
{
    name = "Fraser";
    recordId = 541;
});

以下是组合数组,数组名称为self.arrayOfSearchCombinationsFormed。
  <__NSArrayM 0x1702518b0>(
    ABCD,
    JK,
    AND,
    MIKE,
    ELI,
    STEV,
    FRASE,
    WIILIA
    )

目前的工作代码:

self.filteredContacts = [[NSMutableArray alloc] init];
    NSArray *arrayToTraversed = [[NSArray alloc] initWithArray:self.arrayOfSearchCombinationsFormed];
    for(NSString *combination in arrayToTraversed){
        NSPredicate *predicateInsideLoop = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", combination];
        NSArray *filteredContactByName = [self.listOfAllContacts filteredArrayUsingPredicate:predicateInsideLoop];
        if([filteredContactByName count]>0){
            [self.filteredContacts addObjectsFromArray:filteredContactByName];
        }
        else{
            [self.arrayOfSearchCombinationsFormed removeObject:combination];
        }
    }

目前这个解决方案效率低下,且占用了大量内存。任何帮助将不胜感激。

同时请注意,字典中未找到的任何组合都需要从组合数组中删除。

因此,我的问题是我想要在内存分配方面搜索名称的最有效方法。以便它使用最少的内存。


这个字典非常大,大约有一万个项目,但是数组很小,最多只有50个项目。我不明白NSMutableSet如何能帮助我解决这个问题。 - Harsh
一种优化方法是,一旦联系人符合您的规则,就将其从联系人列表中删除,以减少循环的长度。 - KudoCC
我不能删除原始数组,因为我需要在用户交互中继续搜索列表。 - Harsh
@Harsh 你可以创建原始数组的副本。我认为这是优化算法的一种简单方法,但当符合规则的联系人数量很少时,它并没有太大帮助。 - KudoCC
此外,在原始代码的for循环中尝试使用@autoreleasepool{}。NSMutableSet确保没有重复添加,尽管您已经描述了典型的用例,似乎这并不是一个大问题。 - stevesliva
显示剩余3条评论
5个回答

2

使用(NSPredicate*)predicateWithBlock:方法可能有助于加速搜索。

假设您有一个键数组和一个源数组,您想使用键数组过滤源数组。

NSArray *keysArray = @[@"1",@"2",@"3"];    
NSArray *sourceArray = @[@"12",@"2",@"3",@"1",@"2"];

对于源数组(sourceArray)中的第一个对象 @"12",查看键数组(keysArray),由于@"12"包含@"1",因此您可以停止过滤并保留两个数组的第一个对象。但原始代码使用 @"1" 过滤 sourceArray,结果是@"12"@"1",需要检查每个元素。
您可以参考以下代码:
- (void)searchWithBlock:(NSArray*)keysArray
{
    NSDate *beginDate = [NSDate date];

    NSMutableSet *keySet = [NSMutableSet set];
    NSPredicate *intersectPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
        for (NSString *str in keysArray) {
            NSString *name = evaluatedObject[@"name"];
            NSRange r = [name rangeOfString:str options:NSCaseInsensitiveSearch];
            if (r.location != NSNotFound) {
                [keySet addObject:str];
                return true;
            }
        }
        return false;
    }];

    NSArray *intersect = [self.listOfAllContacts filteredArrayUsingPredicate:intersectPredicate];
    self.filteredContacts = [[NSMutableArray alloc] initWithArray:intersect];

    self.arrayOfSearchCombinationsFormed = [NSMutableArray arrayWithArray:[keySet allObjects]];

    NSDate *endDate = [NSDate date];
    NSTimeInterval interval = [endDate timeIntervalSinceDate:beginDate];
    NSLog(@"interval is %f",interval);

    NSLog(@"intersect %@\n, filtered key array is %@\n", intersect,keySet);
}

过滤时间大约只需要原来的三分之一,内存分配稍微少一些。建议将较大的数据源拆分成较小的块,以使用更少的内存。


1
这个应该能解决问题:
NSString *sourceRegexp =
    [NSString stringWithFormat:@".*%@.*", 
        [combinations componentsJoinedByString:@".*|.*"]];

NSPredicate *sourcePredicate =
    [NSPredicate predicateWithFormat:@"name MATCHES[c] %@", sourceRegexp];

NSArray *filteredSource =
    [source filteredArrayUsingPredicate:sourcePredicate];

NSPredicate *combinationsPredicate =
    [NSPredicate predicateWithFormat:
        @"SUBQUERY(%@, $s, $s.name CONTAINS[c] SELF).@count > 0",
        filteredSource];

NSArray *filteredCombinations =
    [combinations filteredArrayUsingPredicate:combinationsPredicate];

1
我可能误解了问题,但是使用一个带有集合的NSPredicate不可以吗?
NSSet *contactsToSearchFor = [NSSet setWithArray:self.arrayOfSearchCombinationsFormed];
NSPredicate *prediate = [NSPredicate predicateWithFormat:@"name IN[cd] %@", contactsToSearchFor];
NSArray *results = [self.listOfAllContacts filteredArrayUsingPredicate:predicate];

我没有在 XCode 中测试过这个,但应该能够工作。


0

-1
我建议您使用Swift来实现这个目的:它更快,分配的内存也更少。以下是Swift的解决方案:
func filterContacts(contacts: [Dictionary<String, String>], searchCombinations: [String]) -> [Dictionary<String, String>]{
   return contacts.filter { dict in
      let name = dict["name"]!
      for string in searchCombinations{
         if name.rangeOfString(string) != nil { return true }
      }
      return false
   }
}

如果搜索时间很重要,另一个更复杂的解决方案将涉及使用后缀树来存储您的联系人数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接