¿Cómo obtener una lista de elementos comunes de 2 arreglos en Swift?

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 

Explicación de los resultados

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.


Conclusión

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 ).

Observaciones finales

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:

  • 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 stack adicional que cuesta espacio y tiempo ( múltiples veces : 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 }