在两个对象数组之间进行过滤,避免嵌套for循环

时间:2022-08-31 23:05:45

In Swift 4, how do you convert a nested for loop that is checking equality on only one property into a filter?

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

Basic Example:

// 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)
        }
    }
}

This works as expected (foundList contains local[0]) but feeling there should be a 'swift-ier' way to do this?

这按预期工作(foundList包含local [0])但感觉应该有一个'swift-ier'方法来做到这一点?

3 个解决方案

#1


1  

for loops can be rewritten with one for loop + where condition:

for循环可以用一个for循环+重写,其中condition:

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

or combine filter with contains:

或组合过滤器包含:

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

P.S. Also, conform Message struct to Equatable protocol. It allows you to simplify contains method:

附:此外,将Message结构符合Equatable协议。它允许您简化包含方法:

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

using filter:

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

#2


1  

Use 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")]

Note that I removed the initialiser and I’m using Message(...) instead Message.init(...).

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

#3


1  

It should be easy to write a filter for this. I assume you want the local messages that are also on the server.

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

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

If you have a lot of messages, it might be a good idea to create a set of interesting ids.

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

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

That will reduce the big O time complexity because you aren't doing a linear search through the set. The time complexity on contains(where:) for an array is going to be O(n) where n is the number of elements. For a set, Apple documents the complexity of contains() as O(1) Of course, there is an overhead for creating the set and for small n the linear search might be quicker than the set access.

这将减少大的O时间复杂度,因为您没有通过集合进行线性搜索。包含(其中:)数组的时间复杂度将为O(n),其中n是元素的数量。对于集合,Apple将contains()的复杂性记录为O(1)当然,创建集合会产生开销,而对于小n,线性搜索可能比设置访问更快。

#1


1  

for loops can be rewritten with one for loop + where condition:

for循环可以用一个for循环+重写,其中condition:

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

or combine filter with contains:

或组合过滤器包含:

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

P.S. Also, conform Message struct to Equatable protocol. It allows you to simplify contains method:

附:此外,将Message结构符合Equatable协议。它允许您简化包含方法:

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

using filter:

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

#2


1  

Use 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")]

Note that I removed the initialiser and I’m using Message(...) instead Message.init(...).

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

#3


1  

It should be easy to write a filter for this. I assume you want the local messages that are also on the server.

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

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

If you have a lot of messages, it might be a good idea to create a set of interesting ids.

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

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

That will reduce the big O time complexity because you aren't doing a linear search through the set. The time complexity on contains(where:) for an array is going to be O(n) where n is the number of elements. For a set, Apple documents the complexity of contains() as O(1) Of course, there is an overhead for creating the set and for small n the linear search might be quicker than the set access.

这将减少大的O时间复杂度,因为您没有通过集合进行线性搜索。包含(其中:)数组的时间复杂度将为O(n),其中n是元素的数量。对于集合,Apple将contains()的复杂性记录为O(1)当然,创建集合会产生开销,而对于小n,线性搜索可能比设置访问更快。