我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
当前回答
我创建了一个时间复杂度为o(n)的高阶函数。另外,像map这样的功能可以返回您想要的任何类型。
extension Sequence {
func distinct<T,U>(_ provider: (Element) -> (U, T)) -> [T] where U: Hashable {
var uniqueKeys = Set<U>()
var distintValues = [T]()
for object in self {
let transformed = provider(object)
if !uniqueKeys.contains(transformed.0) {
distintValues.append(transformed.1)
uniqueKeys.insert(transformed.0)
}
}
return distintValues
}
}
其他回答
我使用了@Jean-Philippe Pellet的答案,并做了一个数组扩展,对数组进行类似set的操作,同时保持元素的顺序。
/// Extensions for performing set-like operations on lists, maintaining order
extension Array where Element: Hashable {
func unique() -> [Element] {
var seen: [Element:Bool] = [:]
return self.filter({ seen.updateValue(true, forKey: $0) == nil })
}
func subtract(takeAway: [Element]) -> [Element] {
let set = Set(takeAway)
return self.filter({ !set.contains($0) })
}
func intersect(with: [Element]) -> [Element] {
let set = Set(with)
return self.filter({ set.contains($0) })
}
}
对于元素既不是哈希也不是可比的数组(例如复杂对象,字典或结构),这个扩展提供了一种通用的方法来删除重复:
extension Array
{
func filterDuplicate<T:Hashable>(_ keyValue:(Element)->T) -> [Element]
{
var uniqueKeys = Set<T>()
return filter{uniqueKeys.insert(keyValue($0)).inserted}
}
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
{
return filterDuplicate{"\(keyValue($0))"}
}
}
// example usage: (for a unique combination of attributes):
peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }
or...
peopleArray = peopleArray.filterDuplicate{ "\(($0.name, $0.age, $0.sex))" }
您不必为使值可哈希而烦恼,它允许您使用不同的字段组合来实现惟一性。
注:对于更健壮的方法,请参阅下面评论中Coeur提出的解决方案。
stackoverflow.com/a/55684308/1033581
Swift 4的替代方案
在Swift 4.2中,你可以更容易地使用hash类来构建散列。上面的扩展可以改变,以利用这一点:
extension Array
{
func filterDuplicate(_ keyValue:((AnyHashable...)->AnyHashable,Element)->AnyHashable) -> [Element]
{
func makeHash(_ params:AnyHashable ...) -> AnyHashable
{
var hash = Hasher()
params.forEach{ hash.combine($0) }
return hash.finalize()
}
var uniqueKeys = Set<AnyHashable>()
return filter{uniqueKeys.insert(keyValue(makeHash,$0)).inserted}
}
}
调用语法略有不同,因为闭包接收了一个额外的参数,其中包含一个函数,用于散列可变数量的值(这些值必须是单独可散列的)
peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) }
它也可以使用单一唯一性值(使用$1而忽略$0)。
peopleArray = peopleArray.filterDuplicate{ $1.name }
从数组中删除重复项的另一个Swift 3.0解决方案。该解决方案改进了许多已经提出的其他解决方案:
保留输入数组中元素的顺序 线性复杂度O(n):单通滤波器O(n) +集插入O(1)
给定整数数组:
let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]
功能代码:
func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
var unique = Set<T>()
return array.filter { element in
return unique.insert(element).inserted
}
}
orderedSet(array: numberArray) // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
数组扩展代码:
extension Array where Element:Hashable {
var orderedSet: Array {
var unique = Set<Element>()
return filter { element in
return unique.insert(element).inserted
}
}
}
numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
这段代码利用了Set上的插入操作返回的结果,该操作在O(1)上执行,并返回一个元组,指示该项是否被插入,或者该项是否已经存在于Set中。
如果项目在集合中,过滤器将从最终结果中排除它。
让我提出一个类似于斯科特·加德纳的答案,但使用了更简洁的reduce语法。 此解决方案从自定义对象数组中删除重复项(保持初始顺序)
// Custom Struct. Can be also class.
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
let name: String
let lastName : String
}
// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}
let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
CustomStruct(name: "name2", lastName: "lastName1"),
CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3
// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed
如果你想知道这个约简魔法是如何工作的,这里是完全相同的,只是使用了更扩展的约简语法
let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
var newResult = result
if (newResult.contains(category)) {}
else {
newResult.append(category)
}
return newResult
}
uniq2.count // prints 2 - third element has removed
你可以简单地复制粘贴这段代码到Swift Playground中。
我的解决方案,它似乎可以在O(n)时间,因为哈希映射访问是O(1),过滤器是O(n)。它还使用by闭包来选择按顺序区分元素的属性。
extension Sequence {
func distinct<T: Hashable>(by: (Element) -> T) -> [Element] {
var seen: [T: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: by($0)) == nil }
}
}