Podría tener una matriz similar a la siguiente:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
O, en realidad, cualquier secuencia de porciones de datos del mismo tipo. 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 se eliminaron los duplicados de 2, 6 y 15 para asegurarse de que solo hubiera uno de cada elemento idéntico. ¿Swift proporciona una manera de hacer esto fácilmente o tendré que hacerlo yo mismo?
                    
                        arrays
                                swift
                                standard-library
                                
                    
                    
                        Altair357
fuente
                
                fuente

NSSetNSSet es una colección desordenada de objetos, si es necesario mantener el orden NSOrderedSet.$.uniq(array)github.com/ankurp/Dollar#uniq---uniqSetSwift? Podrá proporcionar una lista de elementos únicos y desordenados.Respuestas:
Puede enrollar el suyo, por ejemplo, así:
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 } let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]Y como extensión para
Array:extension Array where Element: Hashable { var uniques: Array { var buffer = Array() var added = Set<Element>() for elem in self { if !added.contains(elem) { buffer.append(elem) added.insert(elem) } } return buffer } }fuente
var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }updateValue(true, forKey: $0)...lugar deaddedDict(true, forKey: $0)...return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }como dices.let uniques = Array(Set(vals))Puede convertir a ay
Setvolver a an deArraynuevo con bastante facilidad:let unique = Array(Set(originals))Esto se no garantiza para mantener el orden original de la matriz.
fuente
originalsno lo sonHashable; soloHashablese pueden agregar tipos de datos a un conjunto, pero cualquier tipo de datos se puede agregar a una matriz.Hay muchas respuestas disponibles aquí, pero me perdí esta extensión simple, adecuada para Swift 2 en adelante:
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 súper simple. Puede llamarse así:
let arrayOfInts = [2, 2, 4, 4] print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]Filtrado basado en propiedades
Para filtrar una matriz según las propiedades, puede utilizar 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 } }Que puede llamar de la siguiente manera:
let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }fuente
extension Array where Element: Equatable) está siendo reemplazada por stackoverflow.com/a/36048862/1033581 que ofrece una solución más poderosa (extension Sequence where Iterator.Element: Equatable).O(n²)rendimiento de tiempo, lo cual es realmente malo para arreglos grandes.O(n²)complejidad aO(n)Utilice
SetoNSOrderedSetpara eliminar duplicados, luego conviértalo de nuevo aArray:let uniqueUnordered = Array(Set(array)) let uniqueOrdered = Array(NSOrderedSet(array: array))fuente
arrayno lo sonHashable; soloHashablese pueden agregar tipos de datos a un conjunto, pero cualquier tipo de datos se puede agregar a una matriz.Si pones ambas extensiones en tu código, se
Hashableusará la versión más rápida cuando sea posible, y laEquatableversión se usará como alternativa.public extension Sequence where Element: Hashable { /// The elements of the sequence, with duplicates removed. /// - Note: Has equivalent elements to `Set(self)`. @available( swift, deprecated: 5.4, message: "Doesn't compile without the constant in Swift 5.3." ) var firstUniqueElements: [Element] { let getSelf: (Element) -> Element = \.self return firstUniqueElements(getSelf) } } public extension Sequence where Element: Equatable { /// The elements of the sequence, with duplicates removed. /// - Note: Has equivalent elements to `Set(self)`. @available( swift, deprecated: 5.4, message: "Doesn't compile without the constant in Swift 5.3." ) var firstUniqueElements: [Element] { let getSelf: (Element) -> Element = \.self return firstUniqueElements(getSelf) } } public extension Sequence { /// The elements of the sequences, with "duplicates" removed /// based on a closure. func firstUniqueElements<Hashable: Swift.Hashable>( _ getHashable: (Element) -> Hashable ) -> [Element] { var set: Set<Hashable> = [] return filter { set.insert(getHashable($0)).inserted } } /// The elements of the sequence, with "duplicates" removed, /// based on a closure. func firstUniqueElements<Equatable: Swift.Equatable>( _ getEquatable: (Element) -> Equatable ) -> [Element] { reduce(into: []) { uniqueElements, element in if zip( uniqueElements.lazy.map(getEquatable), AnyIterator { [equatable = getEquatable(element)] in equatable } ).allSatisfy(!=) { uniqueElements.append(element) } } } }Si el orden no es importante, siempre puede usar este inicializador de conjunto .
fuente
O(n²)rendimiento de tiempo, lo cual es realmente malo para arreglos grandes.O(n)rendimiento Y preservará el orden de los objetos.O(1)respuestas, pero tienen muchos menos votos que la mayoría de lasO(n^2)soluciones ingenuas . Este es particularmente bueno por su simplicidad: stackoverflow.com/a/46354989/3141234Rápido 4
public extension Array where Element: Hashable { func uniqued() -> [Element] { var seen = Set<Element>() return filter{ seen.insert($0).inserted } } }todo lo posible para
inserttambién devolverá una tupla:(inserted: Bool, memberAfterInsert: Set.Element). Ver documentación .Usar el valor devuelto nos ayuda a evitar bucles o realizar cualquier otra operación.
fuente
O(n^2), y nadie se dio cuenta.editar / actualizar Swift 4 o posterior
También podemos extender el
RangeReplaceableCollectionprotocolo para permitir que se use también conStringProtocoltipos: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 } } }let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]"abcdefabcghi".orderedSet // "abcdefghi" "abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"Método de mutación:
var string = "abcdefabcghi" string.removeDuplicates() string // "abcdefghi" var substring = "abcdefabcdefghi".dropFirst(3) // "defabcdefghi" substring.removeDuplicates() substring // "defabcghi"Para Swift 3 haga clic aquí
fuente
reduceimplementación, por lo que ahora la complejidad es diferente.O(n)tiempo), mientras que la versión basada en mapas planos tarda 7,47 veces más en 8 millones de entradas únicas que 1 millón, lo que sugiere que la versión basada en mapas planos se adapta mejor . ¡De alguna manera, la versión basada en mapas planos funciona un poco mejor que elO(n)tiempo!Rápido 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) } } } }fuente
reduce: en general, es sólo una línea más en todo el proyecto para escribir su función como:var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique. Admito que aún no he probado las actuaciones relativas.O(n²)rendimiento de tiempo, lo cual es realmente malo para arreglos grandes.O(n²). No hay nada rápido en esto.reduceoreduce(into:)no haría una diferencia crítica. Reescribir esto para no llamar repetidamentecontainsharía una diferencia MUCHO mayor.Aquí hay una categoría en la
SequenceTypeque conserva el orden original de la matriz, pero usa aSetpara realizar lascontainsbúsquedas para evitar elO(n)costo delcontains(_:)método de Array .public extension Sequence where Element: Hashable { /// Return the sequence 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. func uniqued() -> [Element] { var seen = Set<Element>() return self.filter { seen.insert($0).inserted } } }Si no es Hashable o Equatable, puede pasar un predicado para hacer la verificación de igualdad:
extension Sequence { /// Return the sequence with all duplicates removed. /// /// Duplicate, in this case, is defined as returning `true` from `comparator`. /// /// - note: Taken from stackoverflow.com/a/46354989/3141234 func uniqued(comparator: @escaping (Element, Element) throws -> Bool) rethrows -> [Element] { var buffer: [Element] = [] for element in self { // If element is already in buffer, skip to the next element if try buffer.contains(where: { try comparator(element, $0) }) { continue } buffer.append(element) } return buffer } }Ahora, si no tiene Hashable, pero es Equatable, puede usar este método:
extension Sequence where Element: Equatable { /// Return the sequence with all duplicates removed. /// /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]` /// /// - note: Taken from stackoverflow.com/a/46354989/3141234 func uniqued() -> [Element] { return self.uniqued(comparator: ==) } }Finalmente, puede agregar una versión de ruta clave de uniqued como esta:
extension Sequence { /// Returns the sequence with duplicate elements removed, performing the comparison usinig the property at /// the supplied keypath. /// /// i.e. /// /// ``` /// [ /// MyStruct(value: "Hello"), /// MyStruct(value: "Hello"), /// MyStruct(value: "World") /// ].uniqued(\.value) /// ``` /// would result in /// /// ``` /// [ /// MyStruct(value: "Hello"), /// MyStruct(value: "World") /// ] /// ``` /// /// - note: Taken from stackoverflow.com/a/46354989/3141234 /// func uniqued<T: Equatable>(_ keyPath: KeyPath<Element, T>) -> [Element] { self.uniqued { $0[keyPath: keyPath] == $1[keyPath: keyPath] } } }Puede pegar ambos en su aplicación, Swift elegirá el correcto según el
Iterator.Elementtipo de secuencia .fuente
O(n)solución. Por cierto, puede combinar las operaciones de conjunto "comprobar" e "insertar" en una sola. Ver stackoverflow.com/a/46354989/3141234Inspirándonos en https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift , podemos declarar una herramienta más poderosa que es capaz de filtrar por unicidad en cualquier keyPath. Gracias a los comentarios de Alexander sobre varias respuestas con respecto a la complejidad, las siguientes soluciones deberían ser casi óptimas.
Solución no mutante
Extendemos con una función que puede filtrar la unicidad en cualquier 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 } } }Nota: en el caso de que su objeto no se ajuste a RangeReplaceableCollection, pero sí se ajuste a Sequence, puede tener esta extensión adicional, pero el tipo de retorno siempre será un 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 } } }Uso
Si queremos unicidad para los elementos en sí mismos, como en la pregunta, usamos el 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] */Si queremos unicidad para otra cosa (como para el
idde una colección de objetos), usamos el keyPath de nuestra elección: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}] */Solución mutante
Extendemos con una función de mutación que puede filtrar la unicidad en cualquier 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 } } }Uso
Si queremos unicidad para los elementos en sí mismos, como en la pregunta, usamos el 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] */Si queremos unicidad para otra cosa (como para el
idde una colección de objetos), usamos el keyPath de nuestra elección: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}] */fuente
keyPathvalor predeterminado\.self, porque probablemente esa sea la mayoría de los casos de uso.ElementsiempreHashable. Una alternativa a un valor predeterminado es agregar una sobrecarga simple sin parámetros:extension Sequence where Element: Hashable { func unique() { ... } }Una solución alternativa (si no óptima) a partir de aquí utilizando tipos inmutables en lugar de variables:
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 }Incluido para contrastar el enfoque imperativo de Jean-Pillippe con un enfoque funcional.
Como beneficio adicional, esta función funciona tanto con cadenas como con matrices.
Editar: esta respuesta fue escrita en 2014 para Swift 1.0 (antes
Setestaba disponible en Swift). No requiere conformidad con Hashable y se ejecuta en tiempo cuadrático.fuente
containsagregado como la matriz se ejecutan en O (n). Aunque tiene la ventaja de que solo requiere igualar, no mezclar.filter. Es O (n ^ 2) (que es obligatorio si no desea exigirHashableconformidad), pero al menos debería llamarlo explícitamenteUna solución más de Swift 3.0 para eliminar duplicados de una matriz. Esta solución mejora muchas otras soluciones ya propuestas por:
Dada la matriz 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<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]Código de extensión de matriz:
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]Este código aprovecha el resultado devuelto por la
insertoperación enSet, que se ejecuta enO(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,
filterlo excluirá del resultado final.fuente
deferel código haría la operación de prueba establecida dos veces, una concontainsy otra coninsert. Al leer más la documentación de Swift, descubrí queinsertdevuelve una tupla que indica si el elemento se insertó o no, así que simplifiqué el código eliminando lacontainsmarca.extension Sequence where Iterator.Element: Hashable { ... }insertycontainstienenO(1)complejidad.O(1) + O(1) = O(1). Estas dos operaciones luego se realizannveces (una vez por llamada del cierre pasadofilter, que se llama una vez por elemento) Es decir, si una operación toma una cantidad de tiempo constante independientemente del tamaño de entrada, hacerla dos veces aún hace que tome un tiempo constante eso es independientemente del tamaño de entrada. La complejidad total de esto esO(n).En Swift 5
var array: [String] = ["Aman", "Sumit", "Aman", "Sumit", "Mohan", "Mohan", "Amit"] let uniq = Array(Set(array)) print(uniq)La salida será
["Sumit", "Mohan", "Amit", "Aman"]fuente
veloz 2
con respuesta de función 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 }) }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,9fuente
Boolvalor obviamente es redundante, ya que su código nunca lo lee. Use a enSetlugar de aDictionaryy obtendrá mi voto a favor.Rápido 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] } } }uso:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()o
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()fuente
O(n^2). No hagas esto.Rápido 5
extension Sequence where Element: Hashable { func unique() -> [Element] { NSOrderedSet(array: self as! [Any]).array as! [Element] } }fuente
extension Sequence { // Returns distinct elements based on a key value. func distinct<key: Hashable>(by: ((_ el: Iterator.Element) -> key)) -> [Iterator.Element] { var existing = Set<key>() return self.filter { existing.insert(by($0)).inserted } } }Bool, cuando el único valor que utiliza estrue. Está buscando un "tipo de unidad" (un tipo con un solo valor posible). El tipo de unidad de Swift esVoid, cuyo único valor es()(también conocido como la tupla vacía). Entonces puedes usar[T: Void]. Aunque no debería hacer eso, porque básicamente acaba de inventarSet. Úselo en suSetlugar. Consulte stackoverflow.com/a/55684308/3141234 Elimine esta respuesta.Piense como un programador funcional :)
Para filtrar la lista en función de si el elemento ya se ha producido, necesita el índice. Puede utilizar
enumeratedpara obtener el índice ymapvolver a la lista de valores.let unique = myArray .enumerated() .filter{ myArray.firstIndex(of: $0.1) == $0.0 } .map{ $0.1 }Esto garantiza el pedido. Si no le importa el orden, la respuesta existente de
Array(Set(myArray))es más simple y probablemente más eficiente.ACTUALIZACIÓN: Algunas notas sobre eficiencia y corrección
Algunas personas han comentado sobre la eficiencia. Definitivamente estoy en la escuela de escribir código correcto y simple primero y luego descubrir cuellos de botella más tarde, aunque aprecio que sea discutible si esto es más claro que
Array(Set(array)).Este método es mucho más lento que
Array(Set(array)). Como se señaló en los comentarios, conserva el orden y funciona con elementos que no se pueden usar con hash.Sin embargo, el método de @Alain T también conserva el orden y también es mucho más rápido. Entonces, a menos que su tipo de elemento no sea codificable, o solo necesite un delineador rápido, le sugiero que vaya con su solución.
Aquí hay algunas pruebas en un MacBook Pro (2014) en Xcode 11.3.1 (Swift 5.1) en modo de lanzamiento.
La función del generador de perfiles y dos métodos para comparar:
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} }Y una pequeña variedad de entradas de prueba:
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)}Da como salida:
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 sfuente
Array(Set(myArray)), esto funciona para cosas que no lo sonHashableArray(Set(myArray))del orden de su matriz se mantiene.lastIndex(of:). Estoy totalmente en desacuerdo sobre el punto de claridad frente a optimización en este caso. No creo que esta implementación sea particularmente clara, especialmente en comparación con una solución simple basada en conjuntos. En cualquier caso, dicho código debe extraerse a una función de extensión. Este algoritmo se vuelve básicamente inutilizable incluso con un tamaño de entrada bajo, como de miles a decenas de miles. No es difícil encontrar tales conjuntos de datos, las personas pueden tener miles de canciones, archivos, contactos, etc.Para matrices donde los elementos no son Hashable ni Comparable (por ejemplo, objetos complejos, diccionarios o estructuras), esta extensión proporciona una forma generalizada de eliminar duplicados:
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))" }No tiene que molestarse en hacer valores Hashable y le permite usar diferentes combinaciones de campos para lograr unicidad.
Nota: para un enfoque más sólido, consulte la solución propuesta por Coeur en los comentarios a continuación.
stackoverflow.com/a/55684308/1033581
[EDITAR] Alternativa a Swift 4
Con Swift 4.2 puedes usar la clase Hasher para construir un hash mucho más fácilmente. La extensión anterior podría cambiarse para aprovechar esto:
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} } }La sintaxis de la llamada es un poco diferente porque el cierre recibe un parámetro adicional que contiene una función para aplicar un hash a un número variable de valores (que deben ser hash individualmente)
peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) }También funcionará con un único valor de unicidad (usando $ 1 e ignorando $ 0).
peopleArray = peopleArray.filterDuplicate{ $1.name }fuente
"\()", ya que es posible que no le brinde valores únicos comoHashabledebería. Por ejemplo, si sus elementos se ajustan aPrintabletodos devolviendo lo mismodescription, entonces su filtrado falla.Ta serHashable.Puede usar directamente una colección de conjuntos para eliminar el duplicado y luego volver a convertirlo en una matriz
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]Entonces puedes ordenar tu matriz como quieras
myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]fuente
Versión de sintaxis un poco más concisa de la respuesta Swift 2 de Daniel Krom , con un cierre final y un nombre de argumento abreviado, que parece estar basado en la respuesta original de 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 } }Ejemplo de implementación de un tipo personalizado que se puede usar con
uniq(_:)(que debe ajustarse aHashable, y por lo tantoEquatable, porque seHashableextiendeEquatable):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 cualquierEquatabletipo (o método que devuelva unEquatabletipo, por ejemplo,someMethodThatReturnsAnEquatableType()). El código comentado demuestra la extensión de la verificación de igualdad, dondesomeOtherEquatablePropertyhay otra propiedad de unEquatabletipo (pero también podría ser un método que devuelve unEquatabletipo).id, como se usa en lahashValuepropiedad calculada (requerida para ajustarse aHashable), podría ser cualquierHashable(y por lo tantoEquatable) propiedad (o método que devuelva unHashabletipo).Ejemplo de uso
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) // 3fuente
Bool, cuando el único valor que utiliza estrue. Está buscando un "tipo de unidad" (un tipo con un solo valor posible). El tipo de unidad de Swift esVoid, cuyo único valor es()(también conocido como la tupla vacía). Entonces puedes usar[T: Void]. Aunque no deberías hacer eso, porque básicamente lo acabas de inventarSet. Úselo en suSetlugar. Ver stackoverflow.com/a/55684308/3141234En caso de que necesite valores ordenados, esto funciona (Swift 4)
let sortedValues = Array(Set(array)).sorted()fuente
.sorted()el final. Saludos.[2, 1, 1]? Saldría[1, 2], eso no está ordenado: p[2, 1, 1]. La primera aparición de elementos únicos, en orden, es[2, 1]. Esa es la respuesta correcta. Pero usando su algoritmo (incorrecto), obtiene[1, 2], que está ordenado, pero no está en el orden original correcto.arrayno lo sonHashable; soloHashablese pueden agregar tipos de datos a un conjunto, pero cualquier tipo de datos se puede agregar a una matriz.Hecho....
Ejemplo
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 NSArraysalida de arrayWithoutDuplicates - [1,2,4,6,8]
fuente
Aquí hay una solución que
NStipos heredadosO(n)extension Array where Element: Hashable { var uniqueValues: [Element] { var allowed = Set(self) return compactMap { allowed.remove($0) } } }fuente
aquí he hecho alguna solución O (n) para objetos. No es una solución de pocas líneas, pero ...
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")fuente
Setcon un personalizadoDistinctWrapper, debe usar unDictionaryatributo distinto a los objetos. Cuando sigas esa lógica, eventualmente terminarás implementando [Dictionary.init(_:uniquingKeysWith:)] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… , que ahora está integrado en la biblioteca estándar. Mira lo simple que es esto pastebin.com/w90pVe0pUsé la respuesta de @ Jean-Philippe Pellet e hice una extensión Array que realiza operaciones similares a conjuntos en arreglos, mientras 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) }) } }fuente
Bool, cuando el único valor que utiliza estrue. Está buscando un "tipo de unidad" (un tipo con un solo valor posible). El tipo de unidad de Swift esVoid, cuyo único valor es()(también conocido como la tupla vacía). Entonces puedes usar[T: Void]. Aunque no deberías hacer eso, porque básicamente lo acabas de inventarSet. Úselo en suSetlugar. Ver stackoverflow.com/a/55684308/3141234Esta es una implementación muy simple y conveniente. 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 } }fuente
O(n^2).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)fuente
O(n²).Swift 3 / Swift 4 / Swift 5
Solo un código de línea para omitir los duplicados de Array sin afectar el orden:
let filteredArr = Array(NSOrderedSet(array: yourArray))fuente
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) // = 3Como 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"].
fuente
La forma más sencilla sería utilizar NSOrderedSet, que almacena elementos únicos y conserva el orden de los elementos. Me gusta:
func removeDuplicates(from items: [Int]) -> [Int] { let uniqueItems = NSOrderedSet(array: items) return (uniqueItems.array as? [Int]) ?? [] } let arr = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] removeDuplicates(from: arr)fuente