无法在 Swift 数组扩展中使用包含
Posted
技术标签:
【中文标题】无法在 Swift 数组扩展中使用包含【英文标题】:Unable to use contains within a Swift Array extension 【发布时间】:2014-06-06 21:41:52 【问题描述】:我正在尝试编写一个提供“不同”方法的简单数组扩展。这是我目前所拥有的:
extension Array
func distinct() -> T[]
var rtn = T[]()
for x in self
var containsItem = contains(rtn, x)
if !containsItem
rtn.append(x)
return rtn
问题是'contains'语句失败如下:
找不到接受所提供参数的“包含”的重载
我很确定类型约束是正确的。有什么想法吗?
【问题讨论】:
【参考方案1】:斯威夫特 1.x
array
中的元素不必是Equatable
,即它们不能与==
相提并论。
这意味着您不能为所有个可能的数组编写该函数。而且 Swift 不允许你只扩展数组的一个子集。
这意味着你应该把它写成一个单独的函数(这可能就是为什么contains
也不是一个方法)。
let array = ["a", "b", "c", "a"]
func distinct<T: Equatable>(array: [T]) -> [T]
var rtn = [T]()
for x in array
var containsItem = contains(rtn, x)
if !containsItem
rtn.append(x)
return rtn
distinct(array) // ["a", "b", "c"]
Swift 2/Xcode 7(测试版)更新
Swift 2 支持将扩展限制为协议实现的子集,因此现在允许以下操作:
let array = ["a", "b", "c", "a"]
extension SequenceType where Generator.Element: Comparable
func distinct() -> [Generator.Element]
var rtn: [Generator.Element] = []
for x in self
if !rtn.contains(x)
rtn.append(x)
return rtn
array.distinct() // ["a", "b", "c"]
注意苹果如何使用相同的语法添加SequenceType.contains
。
【讨论】:
如果数组的类型不符合Equatable
,您能否引发异常或以某种方式返回 nil?这将允许扩展 Array,并优雅地处理不受支持的类型。
@AlexWayne 你可以通过对新的本地模板类型进行可选转换来让它静默失败(我不会说它是优雅的)。您可以在我的答案here 上看到一个示例【参考方案2】:
终于知道怎么弄了:
extension Array
func contains<T : Equatable>(obj: T) -> Bool
return self.filter($0 as? T == obj).count > 0
func distinct<T : Equatable>(_: T) -> T[]
var rtn = T[]()
for x in self
if !rtn.contains(x as T)
rtn += x as T
return rtn
以及使用/测试:
let a = [ 0, 1, 2, 3, 4, 5, 6, 1, 2, 3 ]
a.contains(0)
a.contains(99)
a.distinct(0)
不幸的是,我无法找到一种方法来做到这一点,而不必指定随后被忽略的参数。它存在的唯一原因是调用 distinct 的正确形式。对于distinct
,这种方法的主要优势似乎在于它不会将distinct
之类的常用术语转储到全局命名空间中。对于 contains 情况,它看起来确实更自然。
【讨论】:
是的。不幸的是,根据设计,这允许您调用诸如a.distinct("a")
之类的不合逻辑的东西,而对于该函数,编译器可能会给您一个很好的错误。情况总是如此,即使有办法摆脱参数,因为两个 T
s 不会强制匹配。
@nschum 在我的测试中,我无法输入错误的类型。
真的吗?对我来说 [0, 1].distinct("a")
在运行时会导致 EXC_BAD_ACCESS。
@nschum 嗯......我认为我至少尝试过,但现在我似乎看到了同样的事情。不知道:)
如果它在运行时崩溃,那么类型安全的意义何在 :)【参考方案3】:
另一种解决方案是使用 find(Array:[T], obj:T) 函数。它会返回一个可选的 Int,所以你可以做的是
if let foundResult = find(arr, obj) as Int
//obj is contained in arr
else
//obj is not contained in arr.
【讨论】:
【参考方案4】:从 Swift 2 开始,这可以通过协议扩展方法来实现,
例如在所有符合SequenceType
的类型上,其中序列
元素符合Equatable
:
extension SequenceType where Generator.Element : Equatable
func distinct() -> [Generator.Element]
var rtn : [Generator.Element] = []
for elem in self
if !rtn.contains(elem)
rtn.append(elem)
return rtn
例子:
let items = [1, 2, 3, 2, 3, 4]
let unique = items.distinct()
print(unique) // [1, 2, 3, 4]
如果元素被进一步限制为Hashable
,那么你
可以利用Set
类型:
extension SequenceType where Generator.Element : Hashable
func distinct() -> [Generator.Element]
return Array(Set(self))
【讨论】:
以上是关于无法在 Swift 数组扩展中使用包含的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Swift 中扩展 NSManagedObject 以包含 MKAnnotation?