Alt*_*357 222 arrays standard-library swift
我可能有一个如下所示的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似数据类型的序列.我想要做的是确保每个相同的元素只有一个.例如,上面的数组将变为:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了重复的2,6和15,以确保每个相同的元素只有一个.Swift是否提供了一种轻松完成此操作的方法,或者我自己必须这样做?
Ben*_*ard 452
您可以很容易地再次转换为集合并返回到数组:
let unique = Array(Set(originals))
Run Code Online (Sandbox Code Playgroud)
这不能保证维持数组的原始顺序.
Jea*_*let 114
你可以自己滚动,例如像这样(使用Set更新为Swift 1.2):
func uniq<S : SequenceType, T : Hashable where S.Generator.Element == T>(source: S) -> [T] {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]
Run Code Online (Sandbox Code Playgroud)
Swift 3版本:
func uniq<S : Sequence, T : Hashable>(source: S) -> [T] where S.Iterator.Element == T {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
Run Code Online (Sandbox Code Playgroud)
Ant*_*ine 66
这里有很多答案,但我错过了这个简单的扩展,适用于Swift 2及以上版本:
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
Run Code Online (Sandbox Code Playgroud)
使它变得非常简单.可以像这样调用:
let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
Run Code Online (Sandbox Code Playgroud)
根据属性进行过滤
要根据属性筛选数组,可以使用此方法:
extension Array {
func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(lhs: element, rhs: $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
Run Code Online (Sandbox Code Playgroud)
您可以按以下方式拨打电话:
let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
Run Code Online (Sandbox Code Playgroud)
Jes*_*ssy 57
这会获取本页面上已有的一些好信息,并在可能的情况下应用Hashable/Set方法,否则将回退到Equatable代码.
Swift 4更改Hashable扩展名(Equatable保持不变)
public extension Sequence where Element: Hashable {
var firstUniqueElements: [Element] {
var set: Set<Element> = []
return filter { set.insert($0).inserted }
}
}
public extension Sequence where Element: Equatable {
var firstUniqueElements: [Element] {
reduce(into: []) { uniqueElements, element in
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
斯威夫特3
public extension Sequence where Element: Hashable {
var firstUniqueElements: [Element] {
var set: Set<Element> = []
return filter { set.insert($0).inserted }
}
}
public extension Sequence where Element: Equatable {
var firstUniqueElements: [Element] {
reduce(into: []) { uniqueElements, element in
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
斯威夫特2
public extension Sequence where Element: Hashable {
var firstUniqueElements: [Element] {
var set: Set<Element> = []
return filter { set.insert($0).inserted }
}
}
public extension Sequence where Element: Equatable {
var firstUniqueElements: [Element] {
reduce(into: []) { uniqueElements, element in
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Jov*_*vic 56
Swift 3.0
let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
Run Code Online (Sandbox Code Playgroud)
Leo*_*bus 42
将集合元素约束为Equatable,您可以使用contains:
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
mutating func removeDuplicates() {
var set = Set<Element>()
removeAll { !set.insert($0).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是将集合元素约束为Hashable,并使用集合来控制必须映射到结果中的元素:
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Run Code Online (Sandbox Code Playgroud)
使用过滤器:
"abcdefabcghi".orderedSet // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Run Code Online (Sandbox Code Playgroud)
或使用NSOrderedSet:
var string = "abcdefabcghi"
string.removeDuplicates()
string // "abcdefghi"
var substring = "abcdefabcdefghi".dropFirst(3) // "defabcdefghi"
substring.removeDuplicates()
substring // "defabcghi"
Run Code Online (Sandbox Code Playgroud)
使用Swift 4减少(进:)
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
mutating func removeDuplicates() {
var set = Set<Element>()
removeAll { !set.insert($0).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Run Code Online (Sandbox Code Playgroud)
您还可以扩展RangeReplaceableCollection协议以允许它与StringProtocol类型一起使用(字符串和子串):
"abcdefabcghi".orderedSet // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Run Code Online (Sandbox Code Playgroud)
var string = "abcdefabcghi"
string.removeDuplicates()
string // "abcdefghi"
var substring = "abcdefabcdefghi".dropFirst(3) // "defabcdefghi"
substring.removeDuplicates()
substring // "defabcghi"
Run Code Online (Sandbox Code Playgroud)
变异方法
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
mutating func removeDuplicates() {
var set = Set<Element>()
removeAll { !set.insert($0).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
mxc*_*xcl 33
斯威夫特4
public extension Array where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter{ seen.insert($0).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
每一次尝试insert都会返回一个元组:(inserted: Bool, memberAfterInsert: Set.Element).见文档.
使用返回的值有助于我们避免循环或执行任何其他操作.
Ale*_*tin 28
斯威夫特4
保证继续订购.
extension Array where Element: Equatable {
func removingDuplicates() -> Array {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Kau*_*eya 24
uniqued()苹果终于在其算法包中引入了方法。
需要考虑的要点:
Sequence协议的类型。Hashable协议例子:
import Algorithms
let numbers = [1, 2, 3, 3, 2, 3, 3, 2, 2, 2, 1]
print(numbers.uniqued()) // prints [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
更多信息https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md
dea*_*rne 16
这是一个SequenceType保留数组原始顺序的类别,但使用a Set进行contains查找以避免O(n)Array contains(_:)方法的成本.
public extension Array where Element: Hashable {
/// Return the array with all duplicates removed.
///
/// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234, as
/// per @Alexander's comment.
public func uniqued() -> [Element] {
var seen = Set<Element>()
return self.filter { seen.insert($0).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
或者如果你没有Hashable,你可以这样做:
public extension Sequence where Iterator.Element: Equatable {
public func uniqued() -> [Iterator.Element] {
var buffer: [Iterator.Element] = []
for element in self {
guard !buffer.contains(element) else { continue }
buffer.append(element)
}
return buffer
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将这两者都粘贴到您的应用程序中,Swift将根据您的序列Iterator.Element类型选择正确的一个.
Pli*_*kin 12
一个替代(如果不是最优的)从溶液这里使用不可改变的类型,而不是变量:
func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
let s = reduce(seq, S()){
ac, x in contains(ac,x) ? ac : ac + [x]
}
return s
}
Run Code Online (Sandbox Code Playgroud)
包括将Jean-Pillippe的强制性方法与功能性方法进行对比.
作为奖励,此功能适用于字符串和数组!
编辑:这个答案是2014年为Swift 1.0编写的(以前Set在Swift中可用).它不需要Hashable一致性并且在二次时间内运行.
Tim*_* MB 12
像函数式程序员一样思考 :)
要根据元素是否已经出现来过滤列表,您需要索引。您可以使用enumerated获取索引并map返回值列表。
let unique = myArray
.enumerated()
.filter{ myArray.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
Run Code Online (Sandbox Code Playgroud)
这保证了顺序。如果您不介意顺序,那么现有的答案Array(Set(myArray))更简单,可能更有效。
更新:关于效率和正确性的一些说明
一些人对效率发表了评论。我绝对是在先编写正确和简单的代码,然后再找出瓶颈的学校,尽管我很欣赏这是否比Array(Set(array)).
这种方法比Array(Set(array)). 正如评论中所指出的,它确实保留了顺序并适用于不可 Hashable 的元素。
但是,@Alain T 的方法也保留了顺序,而且速度也快了很多。因此,除非您的元素类型不可散列,或者您只需要一个快速的班轮,否则我建议使用他们的解决方案。
以下是在 Xcode 11.3.1 (Swift 5.1) 上的 MacBook Pro (2014) 发布模式下的一些测试。
profiler函数和两种比较方法:
func printTimeElapsed(title:String, operation:()->()) {
var totalTime = 0.0
for _ in (0..<1000) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
totalTime += timeElapsed
}
let meanTime = totalTime / 1000
print("Mean time for \(title): \(meanTime) s")
}
func method1<T: Hashable>(_ array: Array<T>) -> Array<T> {
return Array(Set(array))
}
func method2<T: Equatable>(_ array: Array<T>) -> Array<T>{
return array
.enumerated()
.filter{ array.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
}
// Alain T.'s answer (adapted)
func method3<T: Hashable>(_ array: Array<T>) -> Array<T> {
var uniqueKeys = Set<T>()
return array.filter{uniqueKeys.insert($0).inserted}
}
Run Code Online (Sandbox Code Playgroud)
以及少量的测试输入:
func randomString(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
let shortIntList = (0..<100).map{_ in Int.random(in: 0..<100) }
let longIntList = (0..<10000).map{_ in Int.random(in: 0..<10000) }
let longIntListManyRepetitions = (0..<10000).map{_ in Int.random(in: 0..<100) }
let longStringList = (0..<10000).map{_ in randomString(1000)}
let longMegaStringList = (0..<10000).map{_ in randomString(10000)}
Run Code Online (Sandbox Code Playgroud)
作为输出给出:
Mean time for method1 on shortIntList: 2.7358531951904296e-06 s
Mean time for method2 on shortIntList: 4.910230636596679e-06 s
Mean time for method3 on shortIntList: 6.417632102966309e-06 s
Mean time for method1 on longIntList: 0.0002518167495727539 s
Mean time for method2 on longIntList: 0.021718120217323302 s
Mean time for method3 on longIntList: 0.0005312927961349487 s
Mean time for method1 on longIntListManyRepetitions: 0.00014377200603485108 s
Mean time for method2 on longIntListManyRepetitions: 0.0007293639183044434 s
Mean time for method3 on longIntListManyRepetitions: 0.0001843773126602173 s
Mean time for method1 on longStringList: 0.007168249964714051 s
Mean time for method2 on longStringList: 0.9114790915250778 s
Mean time for method3 on longStringList: 0.015888616919517515 s
Mean time for method1 on longMegaStringList: 0.0525397013425827 s
Mean time for method2 on longMegaStringList: 1.111266262292862 s
Mean time for method3 on longMegaStringList: 0.11214958941936493 s
Run Code Online (Sandbox Code Playgroud)
bla*_*acx 11
extension Sequence where Element: Hashable {
func unique() -> [Element] {
NSOrderedSet(array: self as! [Any]).array as! [Element]
}
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*rom 10
使用uniq功能答案:
func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter({ (v) -> Bool in
return seen.updateValue(true, forKey: v) == nil
})
}
Run Code Online (Sandbox Code Playgroud)
使用:
var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9
Run Code Online (Sandbox Code Playgroud)
小智 10
在斯威夫特 5
var array: [String] = ["Aman", "Sumit", "Aman", "Sumit", "Mohan", "Mohan", "Amit"]
let uniq = Array(Set(array))
print(uniq)
Run Code Online (Sandbox Code Playgroud)
输出将是
["Sumit", "Mohan", "Amit", "Aman"]
Run Code Online (Sandbox Code Playgroud)
Swift 4.x:
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
return Array(Set<Iterator.Element>(self))
}
func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()
Run Code Online (Sandbox Code Playgroud)
要么
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
Run Code Online (Sandbox Code Playgroud)
还有一个Swift 3.0解决方案可以从阵列中删除重复项.此解决方案改进了已提出的许多其他解决方案:
给定整数数组:
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]
Run Code Online (Sandbox Code Playgroud)
功能代码:
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]
Run Code Online (Sandbox Code Playgroud)
数组扩展代码:
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]
Run Code Online (Sandbox Code Playgroud)
此代码利用insert操作返回的结果,该结果Set执行O(1),并返回一个元组,指示项目是否已插入或是否已存在于集合中.
如果该项目在集合中,filter则会将其从最终结果中排除.
受https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift的启发,我们可以声明一个更强大的工具,该工具能够对任何keyPath进行唯一性过滤。感谢Alexander对复杂性的各种回答,以下解决方案应该是最佳的。
我们扩展了一个功能,该功能能够过滤任何keyPath上的唯一性:
extension RangeReplaceableCollection {
/// Returns a collection containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> Self {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
注意:如果您的对象不符合RangeReplaceableCollection,但符合Sequence,则可以具有此附加扩展名,但返回类型始终为Array:
extension Sequence {
/// Returns an array containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> [Element] {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们希望元素本身具有唯一性,如问题所示,我们可以使用keyPath \.self:
let a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let b = a.unique(for: \.self)
/* b is [1, 4, 2, 6, 24, 15, 60] */
Run Code Online (Sandbox Code Playgroud)
如果我们希望其他事物(例如id一组对象的)具有唯一性,则可以使用我们选择的keyPath:
let a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
let b = a.unique(for: \.y)
/* b is [{x 1 y 1}, {x 1 y 2}] */
Run Code Online (Sandbox Code Playgroud)
我们扩展了一个可变功能,该功能能够过滤任何keyPath上的唯一性:
extension RangeReplaceableCollection {
/// Keeps only, in order, the first instances of
/// elements of the collection that compare equally for the keyPath.
mutating func uniqueInPlace<T: Hashable>(for keyPath: KeyPath<Element, T>) {
var unique = Set<T>()
removeAll { !unique.insert($0[keyPath: keyPath]).inserted }
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们希望元素本身具有唯一性,如问题所示,我们可以使用keyPath \.self:
var a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
a.uniqueInPlace(for: \.self)
/* a is [1, 4, 2, 6, 24, 15, 60] */
Run Code Online (Sandbox Code Playgroud)
如果我们希望其他事物(例如id一组对象的)具有唯一性,则可以使用我们选择的keyPath:
var a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
a.uniqueInPlace(for: \.y)
/* a is [{x 1 y 1}, {x 1 y 2}] */
Run Code Online (Sandbox Code Playgroud)
正如 WWDC 2021 上所指出的,Swift 拥有社区开发的算法、集合和数值包。算法包提供了一种uniqued()算法。
这些还不是 Swift 标准库的一部分。目前,您可以从 Apple 的 Github 页面下载它们和/或通过 Swift Package Manager 安装它们。
全球开发者大会视频:
https://developer.apple.com/videos/play/wwdc2021/10256/
GitHub页面:
https://github.com/apple/swift-algorithms
uniqued()和uniqued(on:)文档:
https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md
对于元素既不可散列也不可比较的数组(例如,复杂的对象,字典或结构),此扩展提供了一种通用方法来删除重复项:
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))" }
Run Code Online (Sandbox Code Playgroud)
您不必费心将值设为Hashable,它使您可以使用不同的字段组合来实现唯一性。
注意:有关更健壮的方法,请参阅以下评论中Coeur提出的解决方案。
stackoverflow.com/a/55684308/1033581
[编辑] Swift 4替代
使用Swift 4.2,您可以使用Hasher类轻松构建哈希。可以更改上述扩展名以利用此功能:
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}
}
}
Run Code Online (Sandbox Code Playgroud)
调用语法略有不同,因为闭包收到了一个附加参数,该参数包含一个函数,用于对可变数量的值进行哈希处理(必须分别可哈希)
peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) }
Run Code Online (Sandbox Code Playgroud)
它还将使用单个唯一性值(使用$ 1并忽略$ 0)。
peopleArray = peopleArray.filterDuplicate{ $1.name }
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以直接使用set集合删除重复项,然后将其强制转换为数组
var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)
myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]
Run Code Online (Sandbox Code Playgroud)
然后您可以根据需要订购阵列
myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
Run Code Online (Sandbox Code Playgroud)
完毕....
例子
let array = [1,1,1,1,2,2,2,2,4,6,8]
let orderedSet : NSOrderedSet = NSOrderedSet(array: array)
let arrayWithoutDuplicates : NSArray = orderedSet.array as NSArray
Run Code Online (Sandbox Code Playgroud)
arrayWithoutDuplicates 的输出 - [1,2,4,6,8]