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 tableau qui donne
ouptput = ["mango", "blueberry"]
Je ne peux pas utiliser if contains(array, string)
car je souhaite comparer 2 tableaux.
Vous pouvez également 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 communsNous 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 quelques points de repère (artificiels) avec Int
et court/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
, la conversion en Set
est préférable.
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
L'utilisation de l'approche Array
utilise la recherche "force brute" qui a une complexité temporelleO(N^2)
où N = array1.count = array2.count
est différent de l'approche Set
O(N)
. Cependant, la conversion de Array
à Set
et inversement est très coûteuse pour les données volumineuses, ce qui explique l'augmentation de critical #elements
pour les types de données plus volumineux.
Pour les petits Array
s avec environ 100 éléments, l'approche Array
convient, mais pour les plus grands, vous devez utiliser l'approche Set
.
Si vous souhaitez utiliser cette opération "éléments communs" plusieurs fois, il est conseillé d'utiliser Set
s only si possible (le type des éléments doit être Hashable
).
Une conversion de Array
à Set
est assez chère, alors que la conversion de Set
à Array
est en revanche très peu coûteuse.
Si vous utilisez filter
avec .filter(array1.contains)
, les performances sont plus rapides que .filter{ array1.contains($0) }
puisque:
O(N)
)Convertissez-les en Set et utilisez 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 l'ont mentionné les commentaires ci-dessus).
Vous pouvez utiliser à la place une fonction generic, similaire à celle utilisée par Apple dans leur Swift Tour, et éviter ainsi le casting :
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 n'importe quel tableau (SequenceTypes) et si l'un des éléments est identique, il retourne vrai.
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 supplémentaire ici est que cette fonction fonctionne également avec tous les mêmes tableaux typés. Ainsi, plus tard, si vous devez comparer deux tableaux [myCustomObject]
, une fois qu’ils sont conformes à equatable, vous êtes tous set ! (jeu de mots intentionnel)
Edit: (Pour non éléments communs) vous pouvez 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 moche cependant.
Swift 3.0
Utilisez le filtre pour obtenir les éléments communs 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"]
Une méthode générique, inspirée de Le langage de programmation Swift (Swift 3) exercise:
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]
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)
Utiliser Set et intersection comme suit:
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]))