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,线性搜索可能比设置访问更快。