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.

22
demandé sur halfer 2015-09-07 15:54:43

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 Sets 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))
45
répondu Qbyte 2017-12-12 23:13:39

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))
18
répondu Mousavian 2015-09-07 13:08:35

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.

13
répondu Woodstock 2015-09-13 09:19:32

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

3
répondu Rajamohan S 2018-01-04 04:24:43

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)
2
répondu Mehul Parmar 2018-01-08 10:03:24

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]
2
répondu Nicolas Buquet 2018-09-10 07:31:22