如何在Swift中通过字符串比较来过滤对象

时间:2017-05-01 19:25:37

标签: swift string

我有一系列模特:

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,但匹配例如姓氏或公司名称的一部分。

我缺少什么,并且最好的方法是什么?非常感谢帮助。

2 个答案:

答案 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。然后,对于每个键,我检查联系人属性中是否存在该值。

如果您需要纯功能解决方案,可以使用Setintersection

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
}