Swift Combine实现按优先级多字段分词前缀匹配搜索
解决方案
我们需要针对你的需求重新设计匹配逻辑,同时保留Combine流的原有架构,核心解决分词前缀匹配和短名优先两个核心要求:
1. 搜索词预处理工具函数
先将用户输入拆分为独立的搜索词,过滤无效空值并统一转小写,避免大小写和空格干扰:
func splitSearchTerms(_ input: String) -> [String] { return input.lowercased() .components(separatedBy: .whitespacesAndNewlines) .filter { !$0.isEmpty } }
2. 前缀匹配判断逻辑
实现符合需求的匹配规则:检查所有搜索词是否都是目标字符串中任意单词的前缀(忽略大小写),比如输入brow chai时,brow匹配Brown的前缀、chai匹配Chair的前缀,就判定为匹配:
func matchesAllPrefixTerms(_ target: String, terms: [String]) -> Bool { let targetWords = target.lowercased().components(separatedBy: .whitespacesAndNewlines) return terms.allSatisfy { term in targetWords.contains { word in word.hasPrefix(term) } } }
3. 整合到Combine流的最终实现
修改原函数,加入优先匹配短名的逻辑,只有短名无匹配结果时再匹配长名:
func editingChangedName(_ value: String) { $myName .debounce(for: 0.3, scheduler: RunLoop.main) .receive(on: DispatchQueue.global()) .map { [weak self] filterString in guard let self = self else { return [] } let searchTerms = splitSearchTerms(filterString) // 保留原逻辑:输入长度不足3时返回空 guard !searchTerms.isEmpty, filterString.count >= 3 else { return [] } // 第一步:优先匹配nameShort字段 let shortNameMatches = self.nameArray.filter { matchesAllPrefixTerms($0.nameShort, terms: searchTerms) } // 短名有匹配结果直接返回,无结果再匹配长名 guard shortNameMatches.isEmpty else { return shortNameMatches } // 第二步:匹配nameLong字段 return self.nameArray.filter { matchesAllPrefixTerms($0.nameLong, terms: searchTerms) } } .receive(on: RunLoop.main) .assign(to: &$allNamesArray) }
可选性能优化(大数据量场景)
如果nameArray数据量较大,可提前缓存每个条目拆分后的小写单词数组,避免重复计算:
struct dataSet: Codable, Hashable { let nameShort: String let nameLong: String // 缓存拆分后的小写单词数组 lazy var lowercasedShortWords: [String] = { nameShort.lowercased().components(separatedBy: .whitespacesAndNewlines) }() lazy var lowercasedLongWords: [String] = { nameLong.lowercased().components(separatedBy: .whitespacesAndNewlines) }() }
同步修改匹配函数:
func matchesAllPrefixTerms(_ targetWords: [String], terms: [String]) -> Bool { return terms.allSatisfy { term in targetWords.contains { $0.hasPrefix(term) } } }
过滤时直接使用缓存数组:
let shortNameMatches = self.nameArray.filter { matchesAllPrefixTerms($0.lowercasedShortWords, terms: searchTerms) }
内容的提问来源于stack exchange,提问作者kittonian
相关产品推荐
相关产品推荐

