首页 > 解决方案 > 在给定几个条件的情况下,如何有效地搜索字符串数组中的字符串?

问题描述

我正在尝试过滤字符串数组并根据两个用例返回匹配的字符串。

情况 1:仅当 searchString 位于单词的开头时才匹配。

例如,如果我们有一个数组 -> ["Ralph Breaks The Internet", "Bohemian Rhapsody", "Spider-Man: Into the Spider-Verse"] 并且我们试图将它与搜索字符串 "r" 匹配

在这种情况下,我们应该返回 ["Ralph Breaks The Internet", "Bohemian Rhapsody"],因为 "r" 在开头,如 ralph 中的 r 和 rhapsody 中的 r。但是“蜘蛛侠:平行宇宙”不匹配,因为 r 在中间。

情况 2:如果 searchText 的顺序不准确,也匹配。

例如,如果我们有一个数组 -> ["Ralph Breaks The Internet", "Bohemian Rhapsody", "Spider-Man: Into the Spider-Verse"] 并且我们试图将它与搜索字符串 "Rhapsody Bohemian" 匹配,即使顺序不同,它也应该仍然匹配。

这是我到目前为止所尝试的:

func searchMovies(_ movieNames: [String], with searchText: String) -> [String] {
    
    var matchedMovies = [String]()
    
    for movie in movieNames {
        
        let movieWords = movie.split(separator: " ")
        let searchTextWords = searchText.split(separator: " ")
        var count = searchTextWords.count
        
        loop:
        for word in movieWords {
            for text in searchTextWords {
                let pattern = "\\b\(text)"
                if let _ = word.range(of: pattern, options: [.regularExpression, .caseInsensitive]) {
                    count -= 1
                }
                if count == 0 {
                    matchedMovies.append(movie)
                    break loop
                }
            }
        }
    }
    
    return matchedMovies
}

我知道这不是一种有效的方法。如果有人可以指导我朝某个方向前进,这样我就可以更有效地解决同样的事情,那就太好了。

标签: iosswiftdata-structures

解决方案


对于您的具体情况,您可以像这样格式化您的正则表达式模式:

"^(?=.*\\bRhapsody)(?=.*\\bBohemian).*$"

为了使它灵活,你可以这样写你的函数:

func searchMovies(_ movieNames: [String], with searchText: String) -> [String] {
    // split search text into "words"
    let words: [String] = searchText.components(separatedBy: " ")
    
    // start of pattern string
    var pattern: String = "^"
    
    // for each word in search text
    words.forEach { w in
        // append regex to search for words beginning with word
        pattern += "(?=.*\\b\(w))"
    }
    
    // end of pattern string
    pattern += ".*$"
    
    return movieNames.filter { (movie) -> Bool in
        if let _ = movie.range(of: pattern, options: [.regularExpression, .caseInsensitive]) {
            return true
        }
        return false
    }
}

您可以使用以下命令调用它:

    let a: [String] = [
        "Ralph Breaks The Internet",
        "Bohemian Rhapsody",
        "Spider-Man: Into the Spider-Verse"
    ]
    
    let matchingArray = searchMovies(a, with: "Rhapsody Bohemian")

请注意,这将匹配单词的开头(如您使用“r”所示),因此这将返回相同的结果:

let matchingArray = searchMovies(a, with: "Rhap Boh")

推荐阅读