我有一系列模特:
struct Contact {
var givenName: String!
var familyName: String!
var organizationName: String!
}
我想使用一个UITextField
过滤这些联系人。我目前的问题是在单词之间定义并仅过滤匹配所有单词的联系人。
例如:
var contacts: [Contact] = [Contact(givenName: "David", familyName: "Seek", organizationName: "Aldi"),
Contact(givenName: "Johne", familyName: "Doe", organizationName: "Kaisers"),
Contact(givenName: "Jane", familyName: "Doe", organizationName: "Tengelmann"),
Contact(givenName: "Marco", familyName: "Seidl", organizationName: "Rewe"),
Contact(givenName: "Filip", familyName: "Halbig", organizationName: "Aldi")]
我想输入:David Aldi
,只找到在阿尔迪工作的大卫。我不想看到菲利普也在阿尔迪工作。
此外,如果我输入David Do
,我不希望看到任何联系人,因为没有人会匹配。
func getSearchResults(_ filterKey: String) {
self.presentActivityIndicator()
var processed: Int = 0
self.filteredContacts.removeAll()
DispatchQueue.global(qos: .background).async {
for contact in self.unfilteredContacts {
processed += 1
let lowercasedGivenName = contact.givenName.replacingOccurrences(of: " ", with: "").lowercased()
let lowercasedFamilyName = contact.familyName.replacingOccurrences(of: " ", with: "").lowercased()
let lowercasedOrganizationName = contact.organizationName.replacingOccurrences(of: " ", with: "").lowercased()
let name = lowercasedGivenName.appending(lowercasedFamilyName)
if name.range(of: filterKey.replacingOccurrences(of: " ", with: "")) != nil {
if !self.filteredContacts.contains(contact) {
self.filteredContacts.append(contact)
}
}
for word in filterKey.components(separatedBy: " ") {
if lowercasedOrganizationName.range(of: word.lowercased()) != nil {
if !self.filteredContacts.contains(contact) {
self.filteredContacts.append(contact)
}
}
}
if processed == self.unfilteredContacts.count {
self.reloadTableViewInMainThread()
}
}
}
}
这是我今天尝试过的几种方法之一。但是每次尝试,我一直在结束,过滤掉大卫并输入第二个名字,我发现其他联系人不匹配名字David,但匹配例如姓氏或公司名称的一部分。
我缺少什么,并且最好的方法是什么?非常感谢帮助。
答案 0 :(得分:2)
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
var contactsFiltered = contacts
keys.forEach { key in
contactsFiltered = contactsFiltered.filter {
$0.givenName == key || $0.familyName == key || $0.organizationName == key
}
}
return contactsFiltered
}
我用空格分隔filterKeys
。然后,对于每个键,我检查联系人属性中是否存在该值。
如果您需要纯功能解决方案,可以使用Set
和intersection
:
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
return contacts.filter {
Set([$0.givenName, $0.familyName, $0.organizationName]).intersection(keys).count >= keys.count
}
}
而且,如果您想要使用Mirror
的疯狂解决方案,那么在Contact
中添加新属性时,您无需更新getSearchResults
:
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
return contacts.filter {
let stringAttr = Mirror(reflecting: $0).children.filter { ($0.value as? String) != nil }
let contactValues = stringAttr.map { $0.value as! String }
return Set(contactValues).intersection(keys).count >= keys.count
}
}
谨慎使用我的上一个代码(或从不使用它)
修改强>
用于键中字符串的匹配部分。
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ").map { $0.lowercased() }
var contactsFiltered = contacts
keys.forEach { key in
contactsFiltered = contactsFiltered.filter {
$0.givenName.lowercased().range(of: key) != nil ||
$0.familyName.lowercased().range(of: key) != nil ||
$0.organizationName.lowercased().range(of: key) != nil
}
}
return contactsFiltered
}
答案 1 :(得分:2)
首先将filterKey
分隔为空格分隔的组件
let components = filterKey.components(separatedBy: " ")
然后使用带有闭包语法的filter
函数
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if contact.givenName != string && contact.familyName != string && contact.organizationName != string {
return false
}
}
return true
}
如果联系人匹配所有组件,则闭包返回true
。
包含在它的功能
中func getSearchResults(_ filterKey: String) {
let components = filterKey.components(separatedBy: " ")
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if contact.givenName != string && contact.familyName != string && contact.organizationName != string {
return false
}
}
return true
}
}
注意:
请从不将属性/成员声明为使用init
方法初始化的隐式展开选项。完全合法(并推荐)声明没有惊叹号的成员。但是,如果它们应该是可选的,则将它们声明为真正的可选项(问号)。
如果值不变,则将成员声明为常量(let
)。
struct Contact {
let givenName: String
let familyName: String
let organizationName: String
}
修改:
过滤其属性包含 filterKey
的联系人
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if !contact.givenName.lowercased().contains(string) &&
!contact.familyName.lowercased().contains(string) &&
!contact.organizationName.lowercased().contains(string) {
return false
}
}
return true
}
过滤其属性以 filterKey
开头的联系人
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if !contact.givenName.lowercased().hasPrefix(string) &&
!contact.familyName.lowercased().hasPrefix(string) &&
!contact.organizationName.lowercased().hasPrefix(string) {
return false
}
}
return true
}