arrays - Encuentra elementos duplicados en una matriz usando Swift
ios8 addressbook (10)
¿Cómo encontrar elementos duplicados en la matriz? Tengo una gran variedad de números de teléfono, así que en los números de teléfono debería comenzar a buscar desde el lado derecho hacia el lado izquierdo y encontrar 6 enteros similares. entonces debería imprimirlos.
Basado en la respuesta de Rob , una extensión de matriz para encontrar duplicados es:
extension Array where Element: Hashable {
func duplicates() -> Array {
let groups = Dictionary(grouping: self, by: {$0})
let duplicateGroups = groups.filter {$1.count > 1}
let duplicates = Array(duplicateGroups.keys)
return duplicates
}
}
He encontrado una forma usando Reduc, aquí está el código (Swift 4):
let testNumbers = [1,1,2,3,4,5,2]
let nondupicate = testNumbers.reduce(into: [Int]()) {
if !$0.contains($1) {
$0.append($1)
} else {
print("Found dupicate: /($1)")
}
}
Como efecto secundario, devuelve una matriz que no tiene elementos duplicados.
Puede modificarlo fácilmente para contar números de elementos duplicados, verificar matrices de cadenas, etc.
Igual que en la respuesta de @tikhop , pero como extensión de Array (Swift 3):
extension Array where Element: Comparable & Hashable {
public var duplicates: [Element] {
let sortedElements = sorted { $0 < $1 }
var duplicatedElements = Set<Element>()
var previousElement: Element?
for element in sortedElements {
if previousElement == element {
duplicatedElements.insert(element)
}
previousElement = element
}
return Array(duplicatedElements)
}
}
Para encontrar duplicados, puede crear referencias cruzadas por número de teléfono y luego filtrarlas solo a duplicados. Por ejemplo, considere:
let contacts = [
Contact(name: "Rob", phone: "555-1111"),
Contact(name: "Richard", phone: "555-2222"),
Contact(name: "Rachel", phone: "555-1111"),
Contact(name: "Loren", phone: "555-2222"),
Contact(name: "Mary", phone: "555-3333"),
Contact(name: "Susie", phone: "555-2222")
]
En Swift 4, puedes construir el diccionario de referencia cruzada con:
let crossReference = Dictionary(grouping: contacts, by: { $0.phone })
O
let crossReference = contacts.reduce(into: [String: [Contact]]()) {
$0[$1.phone, default: []].append($1)
}
Luego, para encontrar los duplicados:
let duplicates = crossReference
.filter { $1.count > 1 } // filter down to only those with multiple contacts
.sorted { $0.1.count > $1.1.count } // if you want, sort in descending order by number of duplicates
Claramente, el uso de cualquier tipo de modelo tiene sentido para usted, pero lo anterior usa el siguiente tipo de Contact
:
struct Contact {
let name: String
let phone: String
}
Hay muchas, muchas formas de implementar esto, por lo que no me enfocaré en los detalles de implementación de lo anterior, sino que me centraré en el concepto: crear una matriz original de referencias cruzadas con alguna clave (por ejemplo, un número de teléfono) y luego filtrar los resultados hasta sólo esas claves con valores duplicados.
Parece que desea aplanar esta estructura que refleja los duplicados, en una sola matriz de contactos (no estoy seguro de por qué querría hacer eso, ya que pierde la estructura identificando cuáles son duplicados entre sí), pero Si quieres hacer eso, puedes flatMap
:
let flattenedDuplicates = crossReference
.filter { $1.count > 1 } // filter down to only those with multiple contacts
.flatMap { $0.1 } // flatten it down to just array of contacts that are duplicates of something else
Para las copias de Swift 2 o 3, vea las copias anteriores de esta respuesta .
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 la que puede llamar de la siguiente manera, según el ejemplo de contactos de Rob:
let filteredContacts = myContacts.filterDuplicates { $0.name == $1.name && $0.phone == $1.phone }
Puede implementarlo utilizando "Combinar clasificación" , pero necesita hacer una modificación, durante el paso de combinación debe ignorar los duplicados.
La forma más fácil de encontrar elementos duplicados es si el número de teléfono es solo un número de 6 dígitos y tiene el tipo Int, puede ordenar la matriz de números de teléfono y luego filtrarlos para encontrar duplicados.
var phoneNumbers = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
func findDuplicates(sortedArray array: [Int]) -> [Int]
{
var duplicates: [Int] = []
var prevItem: Int = 0
var addedItem: Int = 0
for item in array
{
if(prevItem == item && addedItem != item)
{
duplicates.append(item)
addedItem = item
}
prevItem = item
}
return duplicates
}
func sortPhoneNumbers(phoneNumbers: [Int]) -> [Int]
{
return phoneNumbers.sorted({ return $0<$1 })
}
sortPhoneNumbers(phoneNumbers)
findDuplicates(sortPhoneNumbers(phoneNumbers))
Además, podría implementar el método findDuplicates de diferentes maneras:
Utilizando Set (Swift 1.2+):
func findDuplicates(array: [Int]) -> [Int]
{
var duplicates = Set<Int>()
var prevItem = 0
for item in array
{
if(prevItem == item)
{
duplicates.insert(item)
}
prevItem = item
}
return Array(duplicates)
}
Y así.
Sentimiento ~ inteligente ~. Dada una serie de Int
s
let x = [1, 1, 2, 3, 4, 5, 5]
let duplicates = Array(Set(x.filter({ (i: Int) in x.filter({ $0 == i }).count > 1})))
// [1, 5]
Tenga en cuenta que esto es tremendamente eficiente para todos los involucrados, incluido el compilador, y usted.
Sólo estoy mostrando.
Edit: jeje alguien bajó esto, lo que me lleva a reiterar, por si acaso: por favor, NO USE ESTO en producción o en cualquier otro lugar.
También tuve un problema similar y lo he superado de la siguiente manera. (Xcode 8.3.2)
let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var b = a // copy-on-write so that "a" won''t be modified
while let c = b.popLast() {
b.forEach() {
if $0 == c {
Swift.print("Duplication: /(c)")
}
}
}
// Duplication: 456789
// Duplication: 123456
El punto es que el número de comparación. Sería más pequeño que los demás.
Supongamos que el número de elementos en la matriz es N. En cada bucle, el número disminuirá en uno. Entonces, el número total será (N-1) + (N-2) + (N-3) + ... + 2 + 1 = N * (N-1) / 2 Cuando N = 10, eso será 9 + 8 + ... = 45
En contraste, el de algunos algoritmos podría ser N * N. Cuando N = 10 será 100.
A pesar de eso, teniendo en cuenta el costo de la copia profunda o la copia superficial, estoy de acuerdo en que la brillante manera de @Patrick Perini sería mejor que esto en algunas situaciones, incluso el número sería N * N.
EDITAR:
Forma alternativa con IteratorProtocol.
let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var i = a.makeIterator()
while let c = i.next() {
var j = i
while let d = j.next() {
if c == d {
Swift.print("Duplication: /(c)")
}
}
}
// Duplication: 123456
// Duplication: 456789
Eso parece más complejo, pero usa la misma idea que antes. Esto no tiene asignaciones de memoria o copias innecesarias.
Mi preocupación es la eficiencia, es decir, una respuesta UI más rápida, una mayor duración de la batería, una huella de memoria más pequeña, etc. Evitar asignaciones de memoria innecesarias y / o copias de memoria que Swift realiza automáticamente en la parte de atrás sería crucial si proporcionamos productos competitivos. (-;
Una respuesta muy simple que conserva todos los duplicados.
let originalNums = [5, 3, 2, 3 , 7 , 5,3]
var nums = Array(originalNums)
let numSet = Set(nums)
for num in numSet {
if let index = nums.index(of: num) {
nums.remove(at: index)
}
}
salida
[3, 5, 3]
La solución de Antoine en la sintaxis Swift 3+
extension Array {
func filterDuplicates(includeElement: @escaping (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] {
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(element, $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}