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.
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)
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)
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)
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.
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
.
É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 .
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é.
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
}
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)
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.