web-dev-qa-db-fra.com

Comment déterminer si un tableau contient tous les éléments d'un autre tableau dans Swift?

J'ai 2 tableaux:

var list:Array<Int> = [1,2,3,4,5]
var findList:Array<Int> = [1,3,5]

Je veux déterminer si list Array contient tous les éléments findList.

Par ailleurs, les éléments peuvent être aussi String ou un autre type.

Comment faire ça?

Je sais que Swift fournit la méthode contains qui fonctionne avec un élément.

28
Maxim Shoustin

Au lieu de parcourir les tableaux et de vous filtrer vous-même, vous pouvez utiliser NSSet pour effectuer tout le travail à votre place.

var list:Array<Int> = [1,2,3,4,5]
var findList:Array<Int> = [1,3,5]

let listSet = NSSet(array: list)
let findListSet = NSSet(array: findList)

let allElemtsEqual = findListSet.isSubsetOfSet(otherSet: listSet)

NSSet est beaucoup plus rapide que les tableaux pour vérifier si elle contient un objet. En fait, c'est ce pour quoi il est conçu.

Édition: / Utilisation de la variable Set de Swift.

let list = [1,2,3,4,5]
let findList = [1,3,5]
let listSet = Set(list)
let findListSet = Set(findList)

let allElemsContained = findListSet.isSubsetOf(listSet)
37
orkoden

Considérez la méthode générique suivante:

func arrayContainsArray<S : SequenceType where S.Generator.Element : Equatable>
      (src:S, lookFor:S) -> Bool{

    for v:S.Generator.Element in lookFor{
      if contains(src, v) == false{
        return false
      }
    }
   return true
}

L'avantage - la méthode s'arrête après le 1er échec et ne continue pas plus de findList


Tests

var listAsInt:Array<Int> = [1,2,3,4,5]
var findListAsInt:Array<Int> = [1,3,5]
var result = arrayContainsArray(listAsInt, findListAsInt) // true

listAsInt:Array<Int> = [1,2,3,4,5]
findListAsInt:Array<Int> = [1,3,5,7,8,9]
result = arrayContainsArray(listAsInt, findListAsInt) // false

var listOfStr:Array<String> = ["aaa","bbb","ccc","ddd","eee"]
var findListOfStr:Array<String> = ["bbb","ccc","eee"]
result = arrayContainsArray(listOfStr, findListOfStr) // true

listOfStr:Array<String> = ["aaa","bbb","ccc","ddd","eee"]
findListOfStr:Array<String> = ["bbb","ccc","eee","sss","fff","ggg"]
result = arrayContainsArray(listOfStr, findListOfStr) // false

(testé sur Beta7)

7
Maxim Shoustin

Dans Swift 3 ou Swift 4, vous pouvez écrire ceci:

extension Array where Element: Equatable {
    func contains(array: [Element]) -> Bool {
        for item in array {
            if !self.contains(item) { return false }
        }
        return true
    }
}

Vous pouvez voir la méthode contient ici

Ceci est juste une extension simple qui vérifie si le tableau que vous donnez est dans le tableau actuel (self)

5
Julien Kode

Vous pouvez utiliser la méthode filter pour renvoyer tous les éléments de findList qui ne sont pas dans list:

let notFoundList = findList.filter( { contains(list, $0) == false } )

puis vérifiez si la longueur du tableau retourné est zéro:

let contained = notFoundList.count == 0

Notez que sa solution parcourt l'intégralité du tableau findList. Elle ne s'arrête donc pas dès qu'un élément non contenu est trouvé. Il doit être utilisé si vous souhaitez également savoir quels éléments ne sont pas contenus. 

Si vous avez juste besoin d'un booléen indiquant si tous les éléments sont contenus ou non, la solution fournie par Maxim Shoustin est plus efficace.

5
Antonio

En complément à Sequence.contains(element) qui gère plusieurs éléments, ajoutez cette extension:

public extension Sequence where Element : Hashable {
    func contains(_ elements: [Element]) -> Bool {
        return Set(elements).isSubset(of:Set(self))
    }
}

Utilisé:

list.contains(findList)

Puisque ceci utilise Set/Hashable, il fonctionne beaucoup mieux que les alternatives Equatable.

4
David James

Étendez la Array avec les méthodes suivantes: 

extension Array {

    func contains<T where T : Equatable>(obj: T) -> Bool {
        return self.filter({$0 as? T == obj}).count > 0
    }

    func isEqualTo< T : Equatable> (comparingArray : [T]) -> Bool {

        if self.count != comparingArray.count {
            return false
        }

        for e in comparingArray {
            if !self.contains(e){
                return false
            }
        }

        return true
    }
}

Un exemple de la façon dont vous pouvez l'utiliser comme ceci: 

if selectedDates.isEqualTo(originalDates) {
    //Arrays the same hide save button
} else {
    //Arrays not the same, show Save & Discard Changes Button (if not shown)
}

Criez à @David Berry pour la méthode contain

2
Ibrahim Yildirim

En ce moment, j'utiliserais probablement quelque chose comme:

let result = list.reduce(true, { $0 ? contains(findList, $1) : $0 })

... mais ensuite, je ne fais que lire cet article , ce qui pourrait me conduire à ce type de solution. Vous pourriez probablement rendre cela plus efficace sans le rendre complètement illisible, mais c'est tôt et je n'ai pas bu mon café.

2
Matt Gibson

Voici la réponse de Maxim Shoustin mise à jour pour Swift 3:

func arrayContainsArray<S : Sequence>
    (src:S, lookFor:S) -> Bool where S.Iterator.Element : Equatable{

    for v:S.Iterator.Element in lookFor{
        if src.contains(v) == false{
            return false
        }
    }
    return true
}
2
Rem-D

allSatisfy semble être ce que vous voulez, en supposant que vous ne pouvez pas conformer vos éléments à Hashable et utilisez l'approche par intersection définie mentionnée par d'autres:

let containsAll = array.allSatisfy(otherArray.contains)
2
Jacob Relkin

Aucune des réponses précédentes ne semble avoir raison.

considérer:

let a = [2,2] let b = [1,2,3]

nous ne dirions pas que b "contient" en fait un, mais si votre algorithme est basé sur la contains(element:) intégrée de for-loop & Swift ou sur un ensemble, le cas ci-dessus passe.

1
Gocy015