【问题标题】:Filter between two arrays of objects avoiding nested for loop在两个对象数组之间过滤,避免嵌套 for 循环
【发布时间】:2018-10-12 10:10:26
【问题描述】:

在 Swift 4 中,如何将只检查一个属性是否相等的嵌套 for 循环转换为过滤器?

基本示例:

// Basic object
struct Message {
    let id: String
    let content: String

    init(id: String, content: String) {
        self.id = id
        self.content = content
    }
}

// Array of objects
let local = [Message.init(id: "1234", content: "test1"), Message.init(id: "2345", content: "test2")]

// Array of objects, one has updated content
let server = [Message.init(id: "1234", content: "testDiff1"), Message.init(id: "3456", content: "test3")]

var foundList = [Message]()

// Nested loop to find based on one property matching
for i in local {
    for j in server {
        if i.id == j.id {
            foundList.append(i)
        }
    }
}

这按预期工作(foundList 包含本地 [0]),但感觉应该有一种“更快捷”的方式来做到这一点?

【问题讨论】:

    标签: arrays swift for-loop filtering


    【解决方案1】:

    for 循环可以用一个 for 循环 + where 条件重写:

    for m in local where server.contains(where: { $0.id == m.id }) {
        foundList.append(m)
    }
    

    或将filtercontains 结合起来:

    foundList = local.filter { m in server.contains(where: { $0.id == m.id }) }
    

    附:此外,将Message 结构体与Equatable 协议一致。它允许您简化contains 方法:

    for m in local where server.contains(m) {
        foundList.append(m)
    }
    

    使用filter:

    foundList = local.filter { server.contains($0) }
    

    【讨论】:

    • 谢谢!两个解释和平等的协议信息。
    【解决方案2】:

    为此编写过滤器应该很容易。我假设您想要也在服务器上的本地消息。

    let m = local.filter 
    {
        localMessage in 
        server.contains(where: { $0.id == localMessage.id })
    }
    

    如果您有很多消息,创建一组有趣的 id 可能是个好主意。

    let filterIds = Set(server.map{ $0.id })
    let m = local.filter { filterIds.contains($0) }
    

    这将减少大 O 时间复杂度,因为您没有在集合中进行线性搜索。数组contains(where:) 的时间复杂度将是 O(n),其中 n 是元素的数量。对于集合,Apple documents the complexity of contains() as O(1) 当然,创建集合会产生开销,对于较小的 n,线性搜索可能比集合更快访问。

    【讨论】:

    • 嗨,杰里米。您能否分享有关contains 复杂性的更多信息?也许链接文档或一些文章?谢谢。
    • @pacification 没问题,我再补充回答
    【解决方案3】:

    使用filter

    import Foundation
    
    struct Message: Equatable {
        let id: String
        let content: String
        static func == (lhs: Message, rhs: Message) -> Bool {
            return lhs.id == rhs.id
        }
    }
    
    let local  = [ Message(id: "1234", content: "test1"),     Message(id: "2345", content: "test2") ]
    let server = [ Message(id: "1234", content: "testDiff1"), Message(id: "3456", content: "test3") ]
    
    let foundList = local.filter { server.contains($0) }
    
    print(foundList) // prints [Message(id: "1234", content: "test1")]
    

    请注意,我删除了初始化程序并使用 Message(...) 而不是 Message.init(...)。

    【讨论】:

    • 正如 JeremyP 指出的那样,这可以避免您键入嵌套循环,但它仍在执行。
    猜你喜欢
    • 2012-06-25
    • 2017-08-27
    • 2017-11-11
    • 2021-08-19
    • 2020-05-21
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多