ios - ¿Cómo obtener la lista de elementos comunes de 2 array en Swift?
array filter swift 4 (6)
Conviértalos a Set y use la función intersect ():
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let fruitsSet = Set(fruitsArray)
let vegSet = Set(vegArray)
let output = Array(fruitsSet.intersect(vegSet))
Tengo dos matrices:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
¿Cómo puedo obtener la lista de elementos comunes en esos dos conjuntos que da
ouptput = ["mango", "blueberry"]
No puedo usar
if contains(array, string)
ya que quiero comparar 2 matrices.
Lo siguiente funciona con swift 4:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
var someHash: [String: Bool] = [:]
fruitsArray.forEach { someHash[$0] = true }
var commonItems = [String]()
vegArray.forEach { veg in
if someHash[veg] ?? false {
commonItems.append(veg)
}
}
print(commonItems)
También puede usar
filter
y
contains
en conjunto:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
// only Swift 1
let output = fruitsArray.filter{ contains(vegArray, $0) }
// in Swift 2 and above
let output = fruitsArray.filter{ vegArray.contains($0) }
// or
let output = fruitsArray.filter(vegArray.contains)
Set
vs
Array
para un solo cálculo de elementos comunes
Consideramos el siguiente fragmento de código:
let array1: Array = ...
let array2: Array = ...
// `Array`
let commonElements = array1.filter(array2.contains)
// vs `Set`
let commonElements = Array(Set(array1).intersection(Set(array2)))
// or (performance wise equivalent)
let commonElements: Array = Set(array1).filter(Set(array2).contains)
Hice algunos puntos de referencia (artificiales) con
Int
y
String
s corto / largo (10 a 100
Character
) (todos generados aleatoriamente).
Siempre uso
array1.count == array2.count
Obtengo los siguientes resultados:
Si tiene
critical #(number of) elements
más que
critical #(number of) elements
es preferible convertir a un
Set
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explicación de los resultados.
El uso del enfoque de
Array
utiliza la búsqueda de "fuerza bruta" que tiene
una complejidad de tiempo
O(N^2)
donde
N = array1.count = array2.count
que está en contraste con el enfoque
Set
O(N)
.
Sin embargo, la conversión de
Array
a
Set
y viceversa es muy costosa para datos grandes, lo que explica el aumento de elementos
critical #elements
para tipos de datos más grandes.
Conclusión
Para los
Array
pequeños con aproximadamente 100 elementos, el enfoque
Array
está bien, pero para los más grandes, debe usar el enfoque
Set
.
Si desea utilizar esta operación de "elementos comunes" varias veces, es recomendable utilizar
Set
s
solo
si es posible (el tipo de elementos debe ser
Hashable
).
Observaciones finales
Una conversión de
Array
a
Set
es algo costosa, mientras que la conversión de
Set
a
Array
es, por el contrario, muy económica.
El uso del
filter
con
.filter(array1.contains)
es más rápido en cuanto al rendimiento que
.filter{ array1.contains($0) }
ya que:
- el último crea un nuevo cierre ( solo una vez ) mientras que el primero solo pasa un puntero de función
-
para el último, la llamada del cierre crea un marco de pila adicional que cuesta espacio y tiempo (
varias veces
:
O(N)
)
Un método genérico, inspirado en el ejercicio del lenguaje de programación Swift (Swift 3) :
func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var common: [T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
common.append(lhsItem)
}
}
}
return common
}
Luego, úsalo así:
var a = [3,88,74]
var b = [1,3,88]
print("commons: /(commonElements(a, b))")
--> commons: [3, 88]
Usando Set y también intersección de la siguiente manera:
func findIntersection (firstArray : [Int], secondArray : [Int]) -> [Int]
{
return [Int](Set<Int>(firstArray).intersection(secondArray))
}
print (findIntersection(firstArray: [2,3,4,5], secondArray: [1,2,3]))
No necesita un conjunto (como se ha mencionado en los comentarios anteriores).
En su lugar, podría usar una función genérica , similar a la que Apple usa en su Swift Tour, y así evitar el lanzamiento :
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
Esta función puede tomar dos matrices (SequenceTypes) y si alguno de sus elementos es el mismo, devuelve verdadero.
Simplemente puede modificar esta función genérica para empaquetar una matriz de cadenas y devolverla en su lugar.
Por ejemplo así:
func arrayOfCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
returnArray.append(lhsItem)
}
}
}
return returnArray
}
Uso como este:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
El beneficio adicional aquí es que esta función también funciona con las mismas matrices escritas.
Más tarde, si necesita comparar dos matrices
[myCustomObject]
, una vez que ambas se ajustan a igual, ¡ya está todo
listo
!
(juego de palabras previsto)
Editar: (para elementos no comunes) podría hacer algo como esto
func arrayOfNonCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
var found = false
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
found = true
break
}
}
if (!found){
returnArray.append(lhsItem)
}
found = false
}
for rhsItem in rhs {
for lhsItem in lhs {
if rhsItem == lhsItem {
found = true
break
}
}
if (!found){
returnArray.append(rhsItem)
}
found = false
}
return returnArray
}
Sin embargo, esta implementación es fea.