web-dev-qa-db-fra.com

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 tableau qui donne 

ouptput = ["mango", "blueberry"]

Je ne peux pas utiliser if contains(array, string) car je souhaite comparer 2 tableaux. 

23
AAA

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 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 quelques points de repère (artificiels) avec Int et court/long Strings (10 à 100 Characters) (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

Explication des résultats

L'utilisation de l'approche Array utilise la recherche "force brute" qui a une complexité temporelleO(N^2)N = array1.count = array2.count est différent de l'approche SetO(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.


Conclusion

Pour les petits Arrays 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 Sets only si possible (le type des éléments doit être Hashable).

Remarques finales

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:

  • le dernier crée une nouvelle fermeture (une seule fois) alors que le premier passe seulement 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))
53
Qbyte

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))
19
Mousavian

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.

13
Woodstock

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"]

4
Rajamohan S

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]
4
Nicolas Buquet

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)
3
Mehul Parmar

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]))
0
hotspring