从数组中删除重复的元素
我可能有一个如下所示的数组:
[1, 4, 2 , 2 , 6 , 24, 15 , 2, 60, 15 , 6 ]
或者,实际上,数据的相似部分的任何序列。 我想要做的是确保每个相同的元素只有一个。 例如,上面的数组将变成:
[1, 4, 2 , 6 , 24, 15 , 60]
请注意,删除了2,6和15的副本,以确保每个相同元素只有一个。 Swift是否提供了一个简单的方法,或者我必须自己做?
你可以推出你自己的,例如像这样( 更新Swift 1.2和Set ):
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]
你可以很容易地转换成一个集合,并返回到一个数组:
let unique = Array(Set(originals))
这不保证保持数组的原始顺序。
这里有很多答案,但是我错过了这个简单的扩展,适合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 } }
使它非常简单。 可以这样调用:
let arrayOfInts = [2, 2, 4, 4] print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
基于属性进行过滤
要根据属性过滤数组,可以使用以下方法:
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 } }
你可以调用如下:
let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
这需要一些本页面已经提供的良好信息,并尽可能使用Hashable / Set方法,否则将返回到Equatable代码。
Swift 3
public extension Sequence where Iterator.Element: Hashable { var uniqueElements: [Iterator.Element] { return Array( Set(self) ) } } public extension Sequence where Iterator.Element: Equatable { var uniqueElements: [Iterator.Element] { return self.reduce([]){ uniqueElements, element in uniqueElements.contains(element) ? uniqueElements : uniqueElements + [element] } } }
Swift 2
public extension SequenceType where Generator.Element: Hashable { var uniqueElements: [Generator.Element] { return Array( Set(self) ) } } public extension SequenceType where Generator.Element: Equatable { var uniqueElements: [Generator.Element] { return self.reduce([]){uniqueElements, element in uniqueElements.contains(element) ? uniqueElements : uniqueElements + [element] } } }
将数组元素约束为Equatable,您可以使用它包含:
extension Array where Element: Equatable { var orderedSet: Array { var array: [Element] = [] return flatMap { if array.contains($0) { return nil } else { array.append($0) return $0 } } } }
另一种选择是将数组元素约束为Hashable,并使用一组来控制哪些元素必须映射到结果中:
extension Array where Element: Hashable { var orderedSet: Array { var set = Set<Element>() return flatMap { set.insert($0).inserted ? $0 : nil } } }
使用过滤器:
extension Array where Element: Hashable { var orderedSet: Array { var set = Set<Element>() return filter { set.insert($0).inserted } } }
或者使用NSOrderedSet:
extension Array where Element: Hashable { var orderedSet: Array { return NSOrderedSet(array: self).array as? Array ?? [] } }
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
从这里使用不可变类型而不是变量的替代(如果不是最优的)解决方案:
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 }
包括比较Jean-Pillippe的强制性方法和功能性方法。
作为一个奖金,这个函数与字符串以及数组一起工作!
迅速2
用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 }) }
使用:
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
Swift 3.0
let uniqueUnordered = Array(Set(array)) let uniqueOrdered = Array(NSOrderedSet(array: array))
这是SequenceType
上的一个类别,它保留了数组的原始顺序,但使用Set
来执行contains
查找,以避免Array的contains(_:)
方法的O(n)
开销。
public extension Sequence where Iterator.Element: Hashable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] var lookup = Set<Iterator.Element>() for element in self { guard !lookup.contains(element) else { continue } buffer.append(element) lookup.insert(element) } return buffer } }
或者如果你没有Hashable,你可以这样做:
public extension Sequence where Iterator.Element: Equatable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] for element in self { guard !buffer.contains(element) else { continue } buffer.append(element) } return buffer } }
你可以将这两个都粘贴到你的应用中,Swift会根据你的序列的Iterator.Element
类型来选择正确的。
您可以直接使用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]
然后你可以按你的意愿来订购你的数组
myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
Daniel Krom的Swift 2答案稍微简洁一些,使用了一个尾随的闭包和速记参数名,这个名字看起来是基于Airspeed Velocity的原始答案的 :
func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] { var seen = [E: Bool]() return source.filter { seen.updateValue(true, forKey: $0) == nil } }
实现可以与uniq(_:)
一起使用的自定义类型的示例(必须符合Hashable
,因此Equatable
,因为Hashable
扩展了Equatable
):
func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool { return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty } struct SomeCustomType { let id: Int // ... } extension SomeCustomType: Hashable { var hashValue: Int { return id } }
在上面的代码中…
在==
的重载中使用的id
可以是任何的Equatable
类型(或返回Equatable
类型的方法,例如someMethodThatReturnsAnEquatableType()
)。 注释代码演示如何扩展相等检查,其中someOtherEquatableProperty
是Equatable
类型的另一个属性(但也可以是返回Equatable
类型的方法)。
在hashValue
计算属性(要求符合Hashable
)中使用的id
可以是任何Hashable
(也Equatable
)属性(或返回Hashable
类型的方法)。
使用uniq(_:)
示例:
var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)] print(someCustomTypes.count) // 4 someCustomTypes = uniq(someCustomTypes) print(someCustomTypes.count) // 3
另外一个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]
这段代码利用了在O(1)
上执行的Set
上的insert
操作返回的结果,并返回一个元组,指示该元素是否被插入,或者是否已经存在于该集合中。
如果项目在设置中,则filter
将从最终结果中排除它。
您可以随时使用字典,因为字典只能保存唯一的值。 例如:
var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"] var datesOnlyDict = NSMutableDictionary() var x = Int() for (x=0;x<(arrayOfDates.count);x++) { let date = arrayOfDates[x] as String datesOnlyDict.setValue("foo", forKey: date) } let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"] println(uniqueDatesArray.count) // = 3
正如你所看到的,结果数组并不总是按照“顺序”。 如果您想对数组进行排序/排序,请添加以下内容:
var sortedArray = sorted(datesOnlyArray) { (obj1, obj2) in let p1 = obj1 as String let p2 = obj2 as String return p1 < p2 } println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]
。
我使用@ Jean-Philippe Pellet的答案,并做了一个数组扩展,在数组上进行类似操作,同时保持元素的顺序。
/// 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) }) } }
让我建议一个类似于斯科特·加德纳的答案的答案,但更简洁的语法使用减少。 此解决方案从自定义对象数组中删除重复项(保持初始顺序)
// 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中,然后玩耍。
这只是一个非常简单和方便的实现。 在具有可相等元素的数组的扩展中计算的属性。
extension Array where Element: Equatable { /// Array containing only _unique_ elements. var unique: [Element] { var result: [Element] = [] for element in self { if !result.contains(element) { result.append(element) } } return result } }
对于元素既不是Hasable也不Comparable的数组(例如复杂的对象,字典或结构体),这个扩展提供了一个删除重复的通用方法:
extension Array { func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element] { var uniqueKeys = Set<String>() return filter{uniqueKeys.insert("\(keyValue($0))").inserted} } } // example usage: (for a unique combination of attributes): peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }
你不必费心使Hashable值,它可以让你使用不同的字段组合的唯一性。
这是我搜索网络后找到的答案,没有找到我正在寻找的东西。 使用一个Set,你可以添加所有的元素与减少。 然后我把结果,并将其转换为一个排序的数组。
let initialArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let distinct2 = initialArray.reduce(Set<Int>(), combine: { (set, current) -> Set<Int> in var tmp = set tmp.insert(current) return tmp }) // distinct2 is now a set containing {2, 4, 60, 6, 15, 24, 1} // Make it into a sorted array let sorted = Array(distinct2).sorted(<) // Returns [1, 2, 4, 6, 15, 24, 60]
在这里我已经为对象做了一些O(n)解决方案。 不是几行解决方案,但…
struct DistinctWrapper <T>: Hashable { var underlyingObject: T var distinctAttribute: String var hashValue: Int { return distinctAttribute.hashValue } } func distinct<S : SequenceType, T where S.Generator.Element == T>(source: S, distinctAttribute: (T) -> String, resolution: (T, T) -> T) -> [T] { let wrappers: [DistinctWrapper<T>] = source.map({ return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0)) }) var added = Set<DistinctWrapper<T>>() for wrapper in wrappers { if let indexOfExisting = added.indexOf(wrapper) { let old = added[indexOfExisting] let winner = resolution(old.underlyingObject, wrapper.underlyingObject) added.insert(DistinctWrapper(underlyingObject: winner, distinctAttribute: distinctAttribute(winner))) } else { added.insert(wrapper) } } return Array(added).map( { return $0.underlyingObject } ) } func == <T>(lhs: DistinctWrapper<T>, rhs: DistinctWrapper<T>) -> Bool { return lhs.hashValue == rhs.hashValue } // tests // case : perhaps we want to get distinct addressbook list which may contain duplicated contacts like Irma and Irma Burgess with same phone numbers // solution : definitely we want to exclude Irma and keep Irma Burgess class Person { var name: String var phoneNumber: String init(_ name: String, _ phoneNumber: String) { self.name = name self.phoneNumber = phoneNumber } } let persons: [Person] = [Person("Irma Burgess", "11-22-33"), Person("Lester Davidson", "44-66-22"), Person("Irma", "11-22-33")] let distinctPersons = distinct(persons, distinctAttribute: { (person: Person) -> String in return person.phoneNumber }, resolution: { (p1, p2) -> Person in return p1.name.characters.count > p2.name.characters.count ? p1 : p2 } ) // distinctPersons contains ("Irma Burgess", "11-22-33") and ("Lester Davidson", "44-66-22")
我相信提供一个uniq()
和uniqInPlace()
函数来删除数组的值是很好的。 这与Swift提供的sort()
和sortInPlace()
函数类似。 此外,因为它是一个数组,它应该保持它的元素的原始顺序。
extension Array where Element: Equatable { public func uniq() -> [Element] { var arrayCopy = self arrayCopy.uniqInPlace() return arrayCopy } mutating public func uniqInPlace() { var seen = [Element]() var index = 0 for element in self { if seen.contains(element) { removeAtIndex(index) } else { seen.append(element) index++ } } } }
你只能在变量数组(即var
uniqInPlace()
上使用uniqInPlace()
,因为你不能改变一个常量数组(即let
)。
一些使用示例:
var numbers = [1, 6, 2, 2, 4, 1, 5] numbers.uniqInPlace() // array is now [1, 6, 2, 4, 5] let strings = ["Y", "Z", "A", "Y", "B", "Y", "Z"] let uniqStrings = strings.uniq() // uniqStrings is now ["Y", "Z", "A", "B"]
Swift 3
基于Jean-Philippe Pellet的回答 ,我更新了Swift 3的语法。
func uniq<S : Sequence, T : Hashable where S.Iterator.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(source: vals) // [1, 4, 2, 6, 24, 15, 60]
在Swift 3.0中,我发现在保持顺序的同时消除了重复的元素,这是最简单,最快速的解决方案:
extension Array where Element:Hashable { var unique: [Element] { var set = Set<Element>() //the unique list kept in a Set for fast retrieval var arrayOrdered = [Element]() //keeping the unique list of elements but ordered for value in self { if !set.contains(value) { set.insert(value) arrayOrdered.append(value) } } return arrayOrdered } }
func removeDublicate (ab: [Int]) -> [Int] { var answer1:[Int] = [] for i in ab { if !answer1.contains(i) { answer1.append(i) }} return answer1 }
用法:
let f = removeDublicate(ab: [1,2,2]) print(f)
斯威夫特4
public extension Array where Element: Hashable { func uniqued() -> [Element] { var seen = Set<Element>() return filter{ seen.insert($0).inserted } } }
- 转换为设置
- 检查元素数是否等于之前的数组
- 如果没有,则从Set创建Array
保留唯一的值并保留数组中的排序 。
(使用Swift 3)
var top3score: [Int] = [] outerLoop: for i in 0..<top10score.count { dlog(message: String(top10score[i])) if top3score.count == 3 { break } for aTop3score in top3score { if aTop3score == top10score[i] { continue outerLoop } } top3score.append(top10score[i]) } print("top10score is \(top10score)") //[14, 5, 5, 5, 3, 3, 2, 2, 2, 2] print("top3score is \(top3score)") //[14, 5, 3]
Swift 3.0
extension Array where Element: Equatable { func withoutDuplicates() -> [Element] { return self.reduce([], { (objects, object) -> [Element] in var objects = objects if !objects.contains(object) { objects.append(object) } return objects }) } }