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 arrays.
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)
He realizado algunos puntos de referencia (artificiales) con Int
y String
corta / larga (10 a 100 Character
) (todos generados aleatoriamente). Siempre uso array1.count == array2.count
Obtengo los siguientes resultados:
Si tiene más de critical #(number of) elements
que convertir a un Set
es preferible
data | critical #elements -------------|-------------------- Int | ~50 short String | ~100 long String | ~200
El uso del enfoque de Array
usa “fuerza bruta” -search que tiene complejidad de tiempo O(N^2)
donde N = array1.count = array2.count
que está en contraste con el enfoque de Set
O(N)
. Sin embargo, la conversión de Array
a Set
y viceversa es muy costosa para datos de gran tamaño, lo que explica el aumento de critical #elements
para tipos de datos más grandes.
Para Array
pequeños con alrededor de 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 aconsejable usar Set
s solo si es posible (el tipo de elementos tiene que ser Hashable
).
Una conversión de Array
a Set
es bastante costosa, mientras que la conversión de Set
a Array
es, por el contrario, muy económica.
Usar filter
con .filter(array1.contains)
es más rápido en rendimiento que .filter{ array1.contains($0) }
ya que:
O(N)
) Convierta 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))
No necesita un conjunto (como han mencionado los comentarios anteriores).
En su lugar, podría utilizar una función genérica , similar a la que utiliza Apple en su Swift Tour, y así evitar el lanzamiento :
func anyCommonElements (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 podría modificar esta función genérica para empaquetar una matriz de cadenas y devolverlas en su lugar.
Por ejemplo, así:
func arrayOfCommonElements (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 todas las mismas matrices tipadas. De modo que más adelante si necesita comparar dos matrices [myCustomObject]
, una vez que ambas se ajustan a equatable, ¡ya está todo listo ! (Juego de palabras intencionado)
Editar: (para elementos no comunes) podrías hacer algo como esto
func arrayOfNonCommonElements (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.
Swift 3.0
Use el filtro para obtener elementos comunes de dos matrices.
let fruitsArray = ["apple", "mango", "blueberry", "orange"] let vegArray = ["tomato", "potato", "mango", "blueberry"] let newArray = fruitsArray.filter { (string) -> Bool in return vegArray.contains(string) } print(newArray) // OR /*let newArray = fruitsArray.filter{vegArray.contains($0)}*/ //Different Element /*let newArray = fruitsArray.filter{!vegArray.contains($0)}*/
Salida:
[“mango”, “arándano”]
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)
Un método genérico, inspirado en el ejercicio del Lenguaje de progtwigción Swift (Swift 3) :
func commonElements(_ 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 }