Как определить, содержит ли один массив все элементы другого массива в Swift?

У меня есть 2 массива:

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

Я хочу определить, содержит ли list Array все элементы findList.

Кстати, элементы могут быть String или другим типом.

Как это сделать?

Я знаю, что Swift предоставляет метод contains, который работает с одним элементом.

Ответы

Ответ 1

Вместо повторного использования массивов и выполнения фильтрации вы можете использовать NSSet для выполнения всей работы для вас.

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 намного быстрее, чем массивы при проверке, содержит ли он какой-либо объект. На самом деле это то, для чего он предназначен.

Изменить: Использование встроенного Swift Set.

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)

Ответ 2

Рассмотрим следующий общий метод:

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
}

Преимущество - метод останавливается после 1-го отказа и не продолжается более findList


Испытания

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

(проверено на Beta7)

Ответ 3

Вы можете использовать метод filter для возврата всех элементов findList, которые не находятся в list:

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

затем проверьте, равна ли длина возвращаемого массива нулевым:

let contained = notFoundList.count == 0

Обратите внимание, что его решение проходит весь массив findList, поэтому он не останавливается, как только найден не содержащий элемент. Его следует использовать, если вы также хотите знать, какие элементы не содержатся.

Если вам просто нужно логическое утверждение о том, содержатся ли все элементы или нет, то решение, предоставленное Максимом Шустин, более эффективно.

Ответ 4

В Swift 3 вы можете написать это:

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

Вы можете увидеть метод contains здесь

Это просто простое расширение, проверяющее, находится ли массив, который вы указываете, в текущем массиве (self)

Ответ 5

Прямо сейчас я бы использовал что-то вроде:

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

... но тогда я просто прочитал эту статью, что может смещать меня в сторону такого решения. Возможно, вы могли бы сделать это более эффективным, не сделав его полностью нечитаемым, но рано, и у меня не было кофе.

Ответ 6

Разверните Array следующими способами:

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
    }
}

Пример того, как вы можете использовать его следующим образом:

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

Перейдите к @David Berry для метода .

Ответ 7

Это Ответ Максима Шустина обновлен для 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
}

Ответ 8

var check = true

for i in findList {
    if list.contains(i) {
        check = check && true
    } else {
        check = false
    }
}

Это работает отлично, если использовать.оператор не требуется.

Ответ 9

В качестве дополнения к Sequence.contains(element) обработке нескольких элементов добавьте это расширение:

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

Б:

list.contains(findList)

Так как это использует Set/Hashable, он работает намного лучше, чем Equatable альтернативы.