Comment obtenir la liste des éléments communs du tableau 2 dans Swift?
J'ai deux tableaux:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
Comment puis-je obtenir la liste des éléments communs dans ces deux tableaux qui donnent
ouptput = ["mango", "blueberry"]
Je ne peux pas utiliser if contains(array, string)
car je veux comparer 2 tableaux.
6 réponses
Vous pouvez aussi utiliser filter
et contains
conjointement:
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
pour un seul calcul d'éléments communs
, Nous considérons l'extrait de code suivant:
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)
J'ai fait des benchmarks (artificiels) avec {[7] } et short / long String
s (10 à 100 Character
s) (Tous générés aléatoirement). J'utilise toujours array1.count == array2.count
J'obtiens les résultats suivants:
Si vous avez plus de critical #(number of) elements
conversion Set
est préférable
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explication des résultats
En utilisant l'approche Array
utilise"force Brute" -recherche qui a complexité temporelle O(N^2)
où {[15] } qui est en contraste avec l'approche Set
O(N)
. Cependant, la conversion de Array
à Set
et retour est très coûteuse pour les grandes données, ce qui explique l'augmentation de critical #elements
pour les types de données plus importants.
Conclusion
Pour les petits Array
s avec environ 100 éléments, l'approche Array
est correcte mais pour les plus grands vous devriez utiliser l'approche Set
.
Si vous souhaitez utiliser cette "éléments communs"-opération plusieurs fois, il est conseillé d'utiliser Set
s seulement, si possible (le type des éléments doit être Hashable
).
Remarques Finales
Une conversion de Array
à Set
est un peu coûteuse alors que la conversion de Set
à Array
est en revanche très peu coûteuse.
L'utilisation de filter
avec .filter(array1.contains)
est plus rapide que .filter{ array1.contains($0) }
depuis:
- le dernier crée une nouvelle fermeture (une seule fois ) alors que le premier ne passe qu'un pointeur de fonction
- pour le dernier l'appel de la fermeture crée une trame de pile supplémentaire qui coûte de l'espace et du temps (plusieurs fois:
O(N)
)
Convertissez - les pour définir et utiliser la fonction 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))
Vous n'avez pas besoin d'un Ensemble (comme les commentaires ci-dessus ont mentionné).
Vous pouvez à la place utiliser une fonction Générique , similaire à celle utilisée par Apple dans leur tournée Swift, et éviter ainsi de lancer :
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
}
Cette fonction peut prendre deux tableaux (SequenceTypes) et si l'un de leurs éléments est le même, il renvoie true.
Vous pouvez simplement modifier cette fonction générique pour empaqueter un tableau de chaînes et le renvoyer à la place.
Par exemple comme ceci:
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
}
Utilisation comme ceci:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
L'avantage ici est que cette fonction fonctionne également avec tous les tableaux typés. Donc, plus tard, si vous avez besoin de comparer deux tableaux [myCustomObject]
, Une fois qu'ils sont tous conformes à equatable, vous êtes tous set ! (jeu de mots)
Edit: (Pour non éléments communs), vous pourriez faire quelque chose comme ceci
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
}
Cette implémentation est laide cependant.
Swift 3.0
Utilisez filter pour obtenir des éléments communs à partir de deux tableaux.
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)}*/
Sortie:
["mangue", "myrtille"]
Ce qui suit fonctionne avec 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)
Une méthode générique, inspirée par le langage de programmation Swift (Swift 3) Exercice:
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
}
Ensuite, utilisez-le comme ceci:
var a = [3,88,74]
var b = [1,3,88]
print("commons: \(commonElements(a, b))")
--> commons: [3, 88]