为什么过滤器无法将结构数组转换为协议类型数组?
Why is filter unable to convert an array of structs to an array of a protocol type?
我有以下代码:
protocol TestProtocol {
var string: String {get}
}
struct TestStructOne : TestProtocol {
let string = "test string"
var stringTwo = "test string three"
}
struct TestStructTwo : TestProtocol {
let string = "test string two"
}
var testStructOnes = [TestStructOne(), TestStructOne(), TestStructOne()]
// works
var protocolArrayOne: [TestProtocol] = [TestProtocol]()
for testStruct in testStructOnes.filter({ [=10=].stringTwo == "test string three" }) {
protocolArrayOne.append(testStruct)
}
// does not work, cannot copnvert value of type '[TestStructOne]' to specified type '[TestProtocol]'
var protocolArrayTwo: [TestProtocol] = testStructOnes.filter({ [=10=].stringTwo == "test string three" })
我不明白为什么最后一行不起作用。有人可以帮我填吗?我看不出它与遍历数组并手动添加每个元素有何不同。
这是由于 Swift 数组在隐式转换其类型时的限制,这是 Swift 中泛型不变性的结果,并进行了更多讨论 .
解决方案的关键是你不能将数组直接从 [TestStructOne]
转换为 [TestProtocol]
– 而是你必须分别转换每个元素,这就是为什么你使用 for 循环的方法并手动将元素附加到新数组。
用函数式编程解决这个问题的最简单方法通常是使用 map
以允许每个元素向上转换为 TestProtocol
(Swift 可以推断出这个来自显式类型注释):
let protocolArrayTwo: [TestProtocol] = testStructOnes.filter {
[=10=].stringTwo == "test string three"
}.map{[=10=]}
但是,在您的情况下,这是低效的,因为您必须遍历数组两次,顺便说一下,这也是您的 for
循环正在做的事情(过滤然后遍历元素)。
相反,您可以使用具有给定 where
条件的 for
循环,以便通过在单次迭代中将它们附加到新数组来过滤出您想要的元素:
var protocolArray = [TestProtocol]()
for testStruct in testStructOnes where testStruct.stringTwo == "test string three" {
protocolArray.append(testStruct)
}
或者如果你喜欢函数式编程,你可以使用 flatMap
来过滤掉你不想要的元素(利用 flatMap
过滤掉 nil
) 并将它们转换为您的协议类型:
let protocolArray : [TestProtocol] = testStructOnes.flatMap {
[=12=].stringTwo == "test string three" ? [=12=] : nil
}
与 map
非常相似,此处的显式类型注释将允许 flatMap
将每个元素单独隐式转换为您的 TestProtocol
。
我有以下代码:
protocol TestProtocol {
var string: String {get}
}
struct TestStructOne : TestProtocol {
let string = "test string"
var stringTwo = "test string three"
}
struct TestStructTwo : TestProtocol {
let string = "test string two"
}
var testStructOnes = [TestStructOne(), TestStructOne(), TestStructOne()]
// works
var protocolArrayOne: [TestProtocol] = [TestProtocol]()
for testStruct in testStructOnes.filter({ [=10=].stringTwo == "test string three" }) {
protocolArrayOne.append(testStruct)
}
// does not work, cannot copnvert value of type '[TestStructOne]' to specified type '[TestProtocol]'
var protocolArrayTwo: [TestProtocol] = testStructOnes.filter({ [=10=].stringTwo == "test string three" })
我不明白为什么最后一行不起作用。有人可以帮我填吗?我看不出它与遍历数组并手动添加每个元素有何不同。
这是由于 Swift 数组在隐式转换其类型时的限制,这是 Swift 中泛型不变性的结果,并进行了更多讨论
解决方案的关键是你不能将数组直接从 [TestStructOne]
转换为 [TestProtocol]
– 而是你必须分别转换每个元素,这就是为什么你使用 for 循环的方法并手动将元素附加到新数组。
用函数式编程解决这个问题的最简单方法通常是使用 map
以允许每个元素向上转换为 TestProtocol
(Swift 可以推断出这个来自显式类型注释):
let protocolArrayTwo: [TestProtocol] = testStructOnes.filter {
[=10=].stringTwo == "test string three"
}.map{[=10=]}
但是,在您的情况下,这是低效的,因为您必须遍历数组两次,顺便说一下,这也是您的 for
循环正在做的事情(过滤然后遍历元素)。
相反,您可以使用具有给定 where
条件的 for
循环,以便通过在单次迭代中将它们附加到新数组来过滤出您想要的元素:
var protocolArray = [TestProtocol]()
for testStruct in testStructOnes where testStruct.stringTwo == "test string three" {
protocolArray.append(testStruct)
}
或者如果你喜欢函数式编程,你可以使用 flatMap
来过滤掉你不想要的元素(利用 flatMap
过滤掉 nil
) 并将它们转换为您的协议类型:
let protocolArray : [TestProtocol] = testStructOnes.flatMap {
[=12=].stringTwo == "test string three" ? [=12=] : nil
}
与 map
非常相似,此处的显式类型注释将允许 flatMap
将每个元素单独隐式转换为您的 TestProtocol
。