我尝试创建Array
的扩展名,该扩展名会根据应用了闭包的项目返回一个新的唯一项目数组。
例如:如果我有一个Apple
数组,其中apple具有属性名称和来源,为了得到每个来源的一个Apple,我会调用apple.uniqued(on: { $0.origin })
这是我到目前为止的代码:
extension Array where Element: Equatable {
func uniqued(on extract: (Element) -> Equatable) { // A
let elementsAndValues = map { (item: $0, extracted: extract($0)) } // 1
var uniqueValues: [Element] = []
var uniqueExtracts: [Equatable] = [] // A
elementsAndValues.forEach { (item, extracted) in
if !uniqueExtracts.contains(extracted) { // 3, B
uniqueValues += [item]
uniqueExtracts += [extracted]
}
}
return uniqueValues
}
}
这应该如下工作:
我得到的错误是:
A)"协议' SomeProtocol'只能用作通用约束,因为它具有Self或相关的类型要求" (两次)
B)"缺少参数标签'其中:'在电话"
我使用的是最新版本的Xcode。任何建议都会有很多帮助。非常感谢。
答案 0 :(得分:5)
您有多个问题混合在一起以创建您所看到的错误。你应该做的是使用通用。
extension Array
{
func uniqued<T:Equatable>(on extract:(Array.Element) -> T) -> [Array.Element]
{
let elementsAndValues = self.map{ (item: $0, extracted: extract($0)) }
var uniqueValues:[Element] = []
var uniqueExtracts:[T] = []
for (item, extracted) in elementsAndValues
{
if !uniqueExtracts.contains(extracted)
{
uniqueValues.append(item)
uniqueExtracts.append(extracted)
}
}
return uniqueValues
}
}
<T:Equatable>
声明符合T
的通用类型参数Equatable
。然后函数签名可以期望一个闭包,它从尖括号中的类型约束返回一些我们知道符合T
的泛型类型Equatable
。您还必须将Equatable
的每次出现更改为通用参数T
,因为Equatable
不是真实类型;见my answer here。如果你这样做,代码应该编译。
你还应该改变一些其他的事情:
不使用elementsAndValues.forEach(:)
,而是使用for <pattern> in list {}
循环。
虽然这是有争议的,但在向数组添加一个元素时,您应该使用Array().append(:)
方法而不是+=
连接。在+=
的情况下,与+
相反,这纯粹是为了传达意图。
您没有为函数声明返回类型,因此编译器假定它返回Void
,因此return uniqueValues
语句将导致编译器错误。在函数中添加-> [Array.Element]
以解决此问题。
where Element:Equatable
作为Array
本身的约束是多余的。您正在使用键函数来确定均衡性,因此元素本身是否相等是无关紧要的。
您可能希望使用Set
或其他散列数据结构而不是uniqueExtracts
数组。测试数组中的成员资格是O(n)
操作。
答案 1 :(得分:1)
我会用group(by:)
函数执行此操作,该函数将按给定键(例如origin
)对序列中的每个元素进行分组,从而产生一个字典映射键到组(元素数组)群组)。从那里,我只是映射字典,只是得到每个组中的第一个元素。
public extension Sequence {
public typealias Element = Iterator.Element
public typealias Group = [Element]
public func group<Key: Hashable>(by deriveKey: (Element) -> Key) -> [Key: Group] {
var groups = [Key: Group]()
for element in self {
let key = deriveKey(element)
if var existingArray = groups[key] { // Group already exists for this key
groups[key] = nil //performance optimisation to prevent CoW
existingArray.append(element)
groups[key] = existingArray
}
else {
groups[key] = [element] // Create new group
}
}
return groups
}
}
struct Apple {
let name: String
let origin: String
}
let apples = [
Apple(name: "Foo", origin: "Origin 1"),
Apple(name: "Bar", origin: "Origin 1"),
Apple(name: "Baz", origin: "Origin 2")
]
let firstAppleInEachOrigin = apples.group(by: {$0.origin}).flatMap{ _, group in group.first }
firstAppleInEachOrigin.forEach{ print($0) }