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
NSSet
NSSet es una colección desordenada de objetos, si es necesario mantener el orden NSOrderedSet.$.uniq(array)
github.com/ankurp/Dollar#uniq---uniqSet
Swift? 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
Set
volver a an deArray
nuevo con bastante facilidad:let unique = Array(Set(originals))
Esto se no garantiza para mantener el orden original de la matriz.
fuente
originals
no lo sonHashable
; soloHashable
se 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
Set
oNSOrderedSet
para eliminar duplicados, luego conviértalo de nuevo aArray
:let uniqueUnordered = Array(Set(array)) let uniqueOrdered = Array(NSOrderedSet(array: array))
fuente
array
no lo sonHashable
; soloHashable
se 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
Hashable
usará la versión más rápida cuando sea posible, y laEquatable
versió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
insert
tambié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
RangeReplaceableCollection
protocolo para permitir que se use también conStringProtocol
tipos: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
reduce
implementació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.reduce
oreduce(into:)
no haría una diferencia crítica. Reescribir esto para no llamar repetidamentecontains
haría una diferencia MUCHO mayor.Aquí hay una categoría en la
SequenceType
que conserva el orden original de la matriz, pero usa aSet
para realizar lascontains
bú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.Element
tipo 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
id
de 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
id
de 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
keyPath
valor predeterminado\.self
, porque probablemente esa sea la mayoría de los casos de uso.Element
siempreHashable
. 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
Set
estaba disponible en Swift). No requiere conformidad con Hashable y se ejecuta en tiempo cuadrático.fuente
contains
agregado 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 exigirHashable
conformidad), 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
insert
operació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,
filter
lo excluirá del resultado final.fuente
defer
el código haría la operación de prueba establecida dos veces, una concontains
y otra coninsert
. Al leer más la documentación de Swift, descubrí queinsert
devuelve una tupla que indica si el elemento se insertó o no, así que simplifiqué el código eliminando lacontains
marca.extension Sequence where Iterator.Element: Hashable { ... }
insert
ycontains
tienenO(1)
complejidad.O(1) + O(1) = O(1)
. Estas dos operaciones luego se realizann
veces (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,9
fuente
Bool
valor obviamente es redundante, ya que su código nunca lo lee. Use a enSet
lugar de aDictionary
y 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 suSet
lugar. 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
enumerated
para obtener el índice ymap
volver 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 s
fuente
Array(Set(myArray))
, esto funciona para cosas que no lo sonHashable
Array(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 comoHashable
debería. Por ejemplo, si sus elementos se ajustan aPrintable
todos devolviendo lo mismodescription
, entonces su filtrado falla.T
a 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 seHashable
extiendeEquatable
):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 cualquierEquatable
tipo (o método que devuelva unEquatable
tipo, por ejemplo,someMethodThatReturnsAnEquatableType()
). El código comentado demuestra la extensión de la verificación de igualdad, dondesomeOtherEquatableProperty
hay otra propiedad de unEquatable
tipo (pero también podría ser un método que devuelve unEquatable
tipo).id
, como se usa en lahashValue
propiedad calculada (requerida para ajustarse aHashable
), podría ser cualquierHashable
(y por lo tantoEquatable
) propiedad (o método que devuelva unHashable
tipo).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) // 3
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 suSet
lugar. 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.array
no lo sonHashable
; soloHashable
se 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 NSArray
salida de arrayWithoutDuplicates - [1,2,4,6,8]
fuente
Aquí hay una solución que
NS
tipos 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
Set
con un personalizadoDistinctWrapper
, debe usar unDictionary
atributo 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 suSet
lugar. 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) // = 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"]
.
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