0

我有以下代码来搜索NSString

for (NSDictionary *obj in data) {
        NSString *objQuestion = [obj objectForKey:@"Question"];
        NSRange dataRange = [objQuestion rangeOfString:searchText options:NSCaseInsensitiveSearch];
        if (dataRange.location != NSNotFound) {
            [filteredData addObject:obj];
        }
    }

这工作正常,但有一个问题。如果objQuestion是:“Green Yellow Red”并且我搜索“Yellow Green Red”,则该对象将不会显示,因为我的搜索顺序不正确。

我将如何更改我的代码,以便无论我以什么顺序搜索单词,对象都会显示?

4

3 回答 3

2

您应该将搜索文本分解为单词并搜索每个单词。

NSArray *wordArray= [searchText componentsSeparatedByString: @" "];
for (NSDictionary *obj in data) {
    NSString *objQuestion = [obj objectForKey:@"Question"];        
    BOOL present = NO;
    for (NSString *s in wordArray) {
        if (s) {                
            NSRange dataRange = [objQuestion rangeOfString:s options:NSCaseInsensitiveSearch];
            if (dataRange.location != NSNotFound) {
               present = YES;
            }
        } 
    }
    if (present) {
        [filteredData addObject:obj];
    }
}
于 2013-07-03T14:26:44.067 回答
0

所以你想基本上做一个关键字搜索?我建议进行正则表达式搜索。其中单词可以按任何顺序排列。

像这样的东西。

(your|test|data)? *(your|test|data)? *(your|test|data)?

您可以在 NSRegularExpressoin 中使用它

NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(your|test|data)? *(your|test|data)? *(your|test|data)?" options:NSRegularExpressionCaseInsensitive error:&error];
int numMatches = [regex numberOfMatchesInString:searchString options:0 range:NSMakeRange(0, [searchString length])];];

这将以有效的方式匹配任何排序。

不确定正则表达式是否适用于 Obj C,因为我现在面前没有 mac,但应该没问题。

于 2013-07-03T14:48:17.753 回答
0

您可能要考虑搜索输入字符串并不总是像您期望的那样干净,并且可能包含标点符号、括号等。

您还想对口音松懈。

我喜欢使用正则表达式来解决这类问题,并且由于您正在寻找一种允许对搜索词进行任意排序的解决方案,因此我们需要重新处理搜索字符串。我们也可以为此使用正则表达式——因此模式是通过正则表达式替换构造的,这不符合原则。您可能需要彻底记录它。

所以这里有一个代码片段可以做这些事情:

// Use the Posix locale as the lowest common denominator of locales to
// remove accents.
NSLocale *enLoc = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US_POSIX"];

// Mixed bag of genres, but for testing purposes we get all the accents we need
NSString *orgString = @"Beyoncé Motörhead Händel"; 

// Clean string by removing accents and upper case letters in Posix encoding
NSString *string = [orgString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch
                                                  locale: enLoc ];

// What the user has typed in, with misplaced umlaut and all
NSString *orgSearchString = @"handel, mötorhead, beyonce";

// Clean the search string, too
NSString *searchString = [orgSearchString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch
                                                              locale: enLoc ];

// Turn the search string into a regex pattern.
// Create a pattern that looks like: "(?=.*handel)(?=.*motorhead)(?=.*beyonce)"
// This pattern uses positive lookahead to create an AND logic that will
// accept arbitrary ordering of the words in the pattern.
// The \b expression matches a word boundary, so gets rid of punctuation, etc.

// We use a regex to create the regex pattern.
NSString *regexifyPattern = @"(?w)(\\W*)(\\b.+?\\b)(\\W*)";

NSString *pattern = [searchString stringByReplacingOccurrencesOfString: regexifyPattern
                                                            withString: @"(?=.*$2)"
                                                               options: NSRegularExpressionSearch
                                                                 range: NSMakeRange(0, searchString.length) ];

NSError *error;
NSRegularExpression *anyOrderRegEx = [NSRegularExpression regularExpressionWithPattern: pattern
                                                                               options: 0
                                                                                 error: &error];

if ( !anyOrderRegEx ) {

    // Regex patterns are tricky, programmatically constructed ones even more.
    // So we check if it went well and do something intelligent if it didn't

    // ...
}

// Match the constructed pattern with the string
NSUInteger numberOfMatches = [anyOrderRegEx numberOfMatchesInString: string
                                                    options: 0
                                                      range: NSMakeRange(0, string.length)];


BOOL found = (numberOfMatches > 0);

在Apple的这份技术说明中讨论了 Posix 语言环境标识符的使用。

理论上,如果用户输入对正则表达式具有特殊含义的字符,则此处存在边缘情况,但由于第一个正则表达式删除了非单词字符,因此应该以这种方式解决。有点计划外的积极副作用,因此可能值得验证。

如果您对基于正则表达式的解决方案不感兴趣,代码折叠对于“正常”的基于 NSString 的搜索可能仍然有用。

于 2013-07-03T16:10:55.380 回答