Eliminar elementos duplicados de una matriz en Swift

Es posible que tenga una matriz que se parece a lo siguiente:

[1, 4, 2 , 2 , 6 , 24, 15 , 2, 60, 15 , 6 ] 

O, realmente, cualquier secuencia de porciones de datos de tipo similar. Lo que quiero hacer es asegurarme de que solo haya uno de cada elemento idéntico. Por ejemplo, la matriz anterior se convertiría en:

 [1, 4, 2 , 6 , 24, 15 , 60] 

Observe que los duplicados de 2, 6 y 15 se eliminaron para garantizar que solo había uno de cada elemento idéntico. ¿Swift proporciona una manera de hacer esto fácilmente, o tendré que hacerlo yo mismo?

Puede rodar el suyo, por ejemplo, de esta manera ( actualizado para Swift 1.2 con Set ):

 func uniq(source: S) -> [T] { var buffer = [T]() var added = Set() 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] 

Swift 3 versión:

 func uniq(source: S) -> [T] where S.Iterator.Element == T { var buffer = [T]() var added = Set() for elem in source { if !added.contains(elem) { buffer.append(elem) added.insert(elem) } } return buffer } 

Puede convertir a un conjunto y volver a una matriz de nuevo con bastante facilidad:

 let unique = Array(Set(originals)) 

Esto no garantiza mantener el orden original de la matriz.

Aquí hay muchas respuestas disponibles, pero me perdí esta simple extensión, adecuada para Swift 2 y posteriores:

 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 } } 

Lo hace super simple Se puede llamar así:

 let arrayOfInts = [2, 2, 4, 4] print(arrayOfInts.removeDuplicates()) // Prints: [2, 4] 

Filtrado basado en propiedades

Para filtrar una matriz basada en propiedades, puede usar este método:

 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 } } 

A lo que puede llamar de la siguiente manera:

 let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo } 

Esto toma parte de la buena información que ya está en esta página, y aplica el enfoque Hashable / Set cuando sea posible, y de lo contrario vuelve al código Equatable.

Swift 4 cambia para la extensión Hashable ( Hashable mantiene igual)

 public extension Sequence where Element: Equatable { var uniqueElements: [Element] { return self.reduce(into: []) { uniqueElements, element in if !uniqueElements.contains(element) { uniqueElements.append(element) } } } } 

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] } } } 

Swift 3.0

 let uniqueUnordered = Array(Set(array)) let uniqueOrdered = Array(NSOrderedSet(array: array)) 

Restringir los elementos de la colección a Equatable que puede usar contiene:

 extension Collection where Element: Equatable { var orderedSet: [Element] { var array: [Element] = [] return compactMap { if array.contains($0) { return nil } else { array.append($0) return $0 } } } } 

Otra opción es restringir el elemento de colección a Hashable y usar un conjunto para controlar qué elementos debe mapear en el resultado:

 extension Collection where Element: Hashable { var orderedSet: [Element] { var set = Set() return compactMap { set.insert($0).inserted ? $0 : nil } } } 

usando el filtro:

 extension Collection where Element: Hashable { var orderedSet: [Element] { var set = Set() return filter { set.insert($0).inserted } } } 

o usando NSOrderedSet:

 extension Array where Element: Hashable { var orderedSet: Array { return NSOrderedSet(array: self).array as? Array ?? [] } } 

Usando Swift 4 reduce (into 🙂

 extension Collection where Element: Hashable { var orderedSet: [Element] { var set: Set = [] return reduce(into: []) { set.insert($1).inserted ? $0.append($1) : () } } } 

 let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60] 

Swift 4

Garantizado para seguir ordenando.

 extension Array where Element: Equatable { func removingDuplicates() -> Array { return reduce(into: []) { result, element in if !result.contains(element) { result.append(element) } } } } 

Swift 4

 public extension Array where Element: Hashable { func uniqued() -> [Element] { var seen = Set() return filter{ seen.insert($0).inserted } } } 

cada bash de insert también devolverá una tupla: (inserted: Bool, memberAfterInsert: Set.Element) . Ver documentación .

Usar el valor devuelto nos ayuda a evitar el bucle o realizar cualquier otra operación.

Una solución alternativa (si no óptima) desde aquí usando tipos inmutables en lugar de variables:

 func deleteDuplicates(seq:S)-> S { let s = reduce(seq, S()){ ac, x in contains(ac,x) ? ac : ac + [x] } return s } 

Incluido para contrastar el enfoque imperativo de Jean-Pillippe con un enfoque funcional.

¡Como una ventaja, esta función funciona tanto con cadenas como con matrices!

veloz 2

con la función de respuesta uniq :

 func uniq(source: S) -> [E] { var seen: [E:Bool] = [:] return source.filter({ (v) -> Bool in return seen.updateValue(true, forKey: v) == nil }) } 

utilizar:

 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 

Aquí hay una categoría en SequenceType que conserva el orden original de la matriz, pero usa un Set para realizar las búsquedas de contenido para evitar el costo O(n) en el método contains(_:) la matriz.

 public extension Sequence where Iterator.Element: Hashable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] var lookup = Set() for element in self { guard !lookup.contains(element) else { continue } buffer.append(element) lookup.insert(element) } return buffer } } 

o si no tiene Hashable, puede hacer esto:

 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 } } 

Puede pegar ambos en su aplicación, Swift elegirá la correcta dependiendo del tipo de Iterator.Element su secuencia.

Puede usar directamente una colección de conjuntos para eliminar el duplicado y luego devolverlo a una matriz

 var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] var mySet = Set(myArray) myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1] 

Entonces puedes ordenar tu matriz como quieras

 myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60] 

Una solución más de Swift 3.0 para eliminar duplicados de una matriz. Esta solución mejora muchas otras soluciones ya propuestas por:

  • Preservar el orden de los elementos en la matriz de entrada
  • Complejidad lineal O (n): filtro de paso único O (n) + inserción de ajuste O (1)

Dado el conjunto de enteros:

 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] 

Código funcional:

 func orderedSet(array: Array) -> Array { var unique = Set() 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] 

Código de extensión de matriz:

 extension Array where Element:Hashable { var orderedSet: Array { var unique = Set() return filter { element in return unique.insert(element).inserted } } } numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45] 

Este código aprovecha el resultado devuelto por la operación de insert en Set , que se ejecuta en O(1) y devuelve una tupla que indica si el elemento se insertó o si ya existía en el conjunto.

Si el artículo estaba en el conjunto, el filter lo excluirá del resultado final.

Una versión de syntax un poco más sucinta de la respuesta Swift 2 de Daniel Krom , usando un nombre de argumento final de taquigrafía y cierre, que parece estar basado en la respuesta original de Airspeed Velocity :

 func uniq(source: S) -> [E] { var seen = [E: Bool]() return source.filter { seen.updateValue(true, forKey: $0) == nil } } 

Ejemplo de implementación de un tipo personalizado que se puede usar con uniq(_:) (que debe ajustarse a Hashable , y por lo tanto Equatable , porque Hashable extiende 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 } } 

En el código anterior …

id , como se usa en la sobrecarga de == , podría ser cualquier tipo Equatable (o método que devuelva un tipo Equatable , por ejemplo, someMethodThatReturnsAnEquatableType() ). El código comentado demuestra la extensión del control de igualdad, donde someOtherEquatableProperty es otra propiedad de un tipo Equatable (pero también podría ser un método que devuelve un tipo Equatable ).

id , tal como se usa en la propiedad calculada de hashValue (requerida para ajustarse a Hashable ), podría ser cualquier Hashable (y por lo tanto Equatable ) (o método que devuelve un tipo Hashable ).

Ejemplo de uso de 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 

Para las matrices donde los elementos no son Hashable ni Comparables (por ejemplo, objetos complejos, diccionarios o estructuras), esta extensión proporciona una forma generalizada de eliminar duplicados:

 extension Array { func filterDuplicate(_ keyValue:(Element)->T) -> [Element] { var uniqueKeys = Set() return filter{uniqueKeys.insert("\(keyValue($0))").inserted} } } // example usage: (for a unique combination of attributes): peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) } 

No tiene que preocuparse por hacer los valores Hashable y le permite usar diferentes combinaciones de campos para la singularidad.

Siempre puede usar un diccionario, porque un diccionario solo puede contener valores únicos. Por ejemplo:

 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 

Como puede ver, la matriz resultante no siempre estará en 'orden'. Si desea ordenar / ordenar la matriz, agregue esto:

 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"] 

.

Utilicé la respuesta de @Jean-Philippe Pellet e hice una extensión Array que realiza operaciones similares a conjuntos en arreglos, mientras se mantiene el orden de los elementos.

 /// 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) }) } } 

Permítanme sugerir una respuesta similar a la respuesta de Scott Gardner, pero con una syntax más lacónica usando reducir. Esta solución elimina duplicados de una matriz de objetos personalizados (manteniendo el orden inicial)

 // 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 

Y solo si te estás preguntando cómo funciona esta reducción de magia, aquí es exactamente lo mismo, pero usando syntax de reducción más reducida

 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 

Simplemente puede copiar y pegar este código en un Swift Playground y jugar.

Esto es solo una implementación muy simple y conveniente. Una propiedad calculada en una extensión de una matriz que tiene elementos equivalentes.

 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 } } 
 func removeDublicate (ab: [Int]) -> [Int] { var answer1:[Int] = [] for i in ab { if !answer1.contains(i) { answer1.append(i) }} return answer1 } 

Uso:

 let f = removeDublicate(ab: [1,2,2]) print(f) 

Aquí está la respuesta que surgió después de buscar en la web y no encontrar lo que estaba buscando. Usando un conjunto, puede agregar todos los elementos con reducir. Luego tomo el resultado y lo convierto en una matriz ordenada.

 let initialArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let distinct2 = initialArray.reduce(Set(), combine: { (set, current) -> Set 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] 

aquí hice una solución O (n) para objetos. No es una solución de pocas líneas, pero …

 struct DistinctWrapper : Hashable { var underlyingObject: T var distinctAttribute: String var hashValue: Int { return distinctAttribute.hashValue } } func distinct(source: S, distinctAttribute: (T) -> String, resolution: (T, T) -> T) -> [T] { let wrappers: [DistinctWrapper] = source.map({ return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0)) }) var added = Set>() 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 == (lhs: DistinctWrapper, rhs: DistinctWrapper) -> 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") 

Creo que sería bueno ofrecer una función uniq() y uniqInPlace() para mutar una matriz eliminando sus valores. Esto funciona de manera similar a la función sort() y sortInPlace() proporcionada por Swift. Además, dado que es una matriz, debe mantener su orden original de elementos.

 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++ } } } } 

Solo puede usar uniqInPlace() en una matriz variable (es decir, var ) ya que no puede mutar una matriz constante (es decir, let ).

Algunos ejemplos de uso:

 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

Basado en la respuesta de Jean-Philippe Pellet , actualicé su syntax para Swift 3.

 func uniq(source: S) -> [T] { var buffer = [T]() var added = Set() 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] 

En Swift 3.0, la solución más simple y rápida que he encontrado para eliminar los elementos duplicados y mantener el orden:

 extension Array where Element:Hashable { var unique: [Element] { var set = Set() //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 } } 

He hecho una extensión tan simple como sea posible para ese propósito.

 extension Array where Element: Equatable { func containsHowMany(_ elem: Element) -> Int { return reduce(0) { $1 == elem ? $0 + 1 : $0 } } func duplicatesRemoved() -> Array { return self.filter { self.containsHowMany($0) == 1 } } mutating func removeDuplicates() { self = self.duplicatesRemoved(() } } 

Puede usar duplicatesRemoved() para obtener una nueva matriz, cuyos elementos duplicados se eliminan, o removeDuplicates() para mutarse. Ver:

 let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8] let noDuplicates = arr.duplicatesRemoved() print(arr) // [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8] print(noDuplicates) // [1, 2, 3, 4, 5, 6, 7, 8] arr.removeDuplicates() print(arr) // [1, 2, 3, 4, 5, 6, 7, 8] 

Esto también funciona (Swift 4)

let sortedValues = Array(Set(array)).sorted()

Swift 4.x:

 extension Sequence where Iterator.Element: Hashable { func unique() -> [Iterator.Element] { return Array(Set(self)) } func uniqueOrdered() -> [Iterator.Element] { return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] } } } 

uso:

 ["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique() 

o

 ["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered() 

Swift 4.2 Probado

 extension Sequence where Iterator.Element: Hashable { func unique() -> [Iterator.Element] { var seen: [Iterator.Element: Bool] = [:] return self.filter { seen.updateValue(true, forKey: $0) == nil } } } 

Preserve valores únicos y conserve la ordenación en una matriz.

(usando Swift 3)

  var top3score: [Int] = [] outerLoop: for i in 0.. 
    Intereting Posts