这是我的代码(简体代码):
struct SomeStruct {
let id: Int
let age: Int
}
extension SomeStruct: Hashable {
var hashValue: Int {
return id.hashValue * age.hashValue
}
static func ==(lhs: SomeStruct, rhs: SomeStruct) -> Bool {
return lhs.id == rhs.id && lhs.age == rhs.age
}
}
struct Calculator {
let struct1: [SomeStruct]
let struct2: [SomeStruct]
func uniqueById() {
let struct3 = Set(struct2).union(Set(struct1))
// I want to union it by property 'id' only.
// If the property 'id' is equal for both objects,
// the object in struct2 should be used (since that can have a different age property)
}
}
SomeStruct
是生成的struct
,我不想编辑。我想为Set
创建一个基于以下1个属性的SomeStruct
:id
。为此,我认为我需要一个自定义的Comparator
,just as Java has。有什么迅捷的方法吗?这是我唯一能想到的,但是我想知道是否有更好的方法:
struct SomeStructComparatorById: Hashable {
let someStruct: SomeStruct
var hashValue: Int {
return someStruct.id.hashValue
}
static func ==(lhs: SomeStructComparatorById, rhs: SomeStructComparatorById) -> Bool {
return lhs.someStruct.id == rhs.someStruct.id
}
}
答案 0 :(得分:1)
简短的回答是“否”。 Swift集没有任何方法可以接受自定义比较器,并且如果您绝对必须具有Set,则包装器想法是唯一的实现方法。我对一套的要求表示怀疑。
建议不要使用计算器中的Set,而建议使用字典。
您可以使用字典来生成一个数组,其中每个项目都有唯一的ID ...
let struct3 = Dictionary(grouping: struct1 + struct2, by: { $0.id })
.compactMap { $0.value.max(by: { $0.age < $1.age })}
或者您可以将元素保留在[Int:SomeStruct]字典中:
let keysAndValues = (struct1 + struct2).map { ($0.id, $0) }
let dictionary = Dictionary(keysAndValues, uniquingKeysWith: { lhs, rhs in
lhs.age > rhs.age ? lhs : rhs
})
答案 1 :(得分:1)
您可以执行以下操作:
var setOfIds: Set<Int> = []
var struct3 = struct2.filter { setOfIds.insert($0.id).inserted }
struct3 += struct1.filter { setOfIds.insert($0.id).inserted }
结果将是SomeStruct
的数组,所有元素都具有唯一的id
。
您可以将其定义为自定义运算符:
infix operator *>
func *> (lhs: [SomeStruct], rhs: [SomeStruct]) -> [SomeStruct] {
var setOfIds: Set<Int> = []
var union = lhs.filter { setOfIds.insert($0.id).inserted }
union += rhs.filter { setOfIds.insert($0.id).inserted }
return union
}
您的代码将如下所示:
func uniqueById() {
let struct3 = struct2 *> struct1
//use struct3
}
答案 2 :(得分:1)
首先,我认为这不适用于Java。 addAll()
不需要比较器(contains
等也没有)。比较器用于排序,而不是相等。从概念上讲,这打破了Set在任何语言下的工作方式。除非在所有情况下都可以互换,否则两个项目不是“相等”的。
这告诉我们我们在这里不需要Set。您想要的是基于某些键的唯一性。那是字典(正如丹尼尔所讨论的)。
您可以只使用“ id-> age”字典或“ id-> struct-of-other-properties”字典作为主要数据类型(而不是使用Array)。或者,您可以将Array变成这样的临时字典:
extension Dictionary {
init<S>(_ values: S, uniquelyKeyedBy keyPath: KeyPath<S.Element, Key>)
where S : Sequence, S.Element == Value {
let keys = values.map { $0[keyPath: keyPath] }
self.init(uniqueKeysWithValues: zip(keys, values))
}
}
并像这样合并它们:
let dict1 = Dictionary(struct1, uniquelyKeyedBy: \.id)
let dict2 = Dictionary(struct2, uniquelyKeyedBy: \.id)
let merged = dict1.merging(dict2, uniquingKeysWith: { old, new in old }).values
这将merged
保留为[SomeStruct]
。
请注意,此Dictionary(uniquelyKeyedBy:)
与Dictionary(uniqueKeysWithValues:)
具有相同的前提条件。如果存在重复的密钥,则是编程错误,并且会引发前提条件失败。