【问题标题】:How to determine if one array contains all elements of another array in Swift?如何确定一个数组是否包含 Swift 中另一个数组的所有元素?
【发布时间】:2014-11-01 03:00:31
【问题描述】:

我有 2 个数组:

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

我想确定list 数组是否包含所有findList 元素。

顺便说一句,元素也可能是String 或其他类型。

怎么做?

我知道 Swift 提供了contains 方法可以处理一个项目。

【问题讨论】:

  • 当心,几个答案建议使用Equatable 一致性的解决方案,其产生> = O(n) 性能。更喜欢使用具有Hashable 一致性的Set,这在许多情况下比使用Equatable 快一个数量级。我在下面添加了一个“包含”扩展名。

标签: ios arrays swift


【解决方案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)
//**Swift 4.2 and Above**
let allElemsContained = findListSet.isSubset(of: listSet)

//below versions
//let allElemsContained = findListSet.isSubsetOf(listSet)

【讨论】:

  • 这两个集合怎么相等?据我所知,这只是从每个数组中生成一个唯一的集合,然后比较它们?
  • 集合相等。她们不一样。 listSet == findListSet 是假的。
  • 你是完全正确的。我再次阅读了这个问题,意识到我误解了它并且使用了错误的方法。正确的使用方法是isSubsetOfSet(_ otherSet:)。谢谢。
  • 您现在可以为此使用 Swift 的内置 Set 类型。
  • 这会给出let list = [1,2,3,4,5]let findList = [1,3,3,5] 的预期结果吗?在使用Set 时,findListlist 的子集,但在仅比较数组时并非如此。
【解决方案2】:

allSatisfy 似乎是您想要的,假设您不能使您的元素符合 Hashable 并使用其他人提到的设置交集方法:

let containsAll = subArray.allSatisfy(largerArray.contains)

【讨论】:

  • 如果想要的子数组包含在更大数组中不重复的重复元素,这将不成立。
【解决方案3】:

Swift 3Swift 4 中,您可以这样写:

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

可以看到包含方法here

这只是一个简单的扩展,检查你给的数组是否在当前数组(self)中

【讨论】:

    【解决方案4】:

    可以使用filter方法返回findList中所有不在list中的元素:

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

    然后检查返回数组的长度是否为零:

    let contained = notFoundList.count == 0
    

    请注意,他的解决方案会遍历整个findList 数组,因此它不会在找到未包含的元素后立即停止。如果您还想知道不包含哪些元素,则应该使用它。

    如果您只需要一个布尔值来说明是否包含所有元素,那么 Maxim Shoustin 提供的解决方案效率更高。

    【讨论】:

    • 在 swift 4 中你可以这样做: var ts = findList.filter{ !list.contains($0) }
    【解决方案5】:

    考虑以下通用方法:

    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
    }
    

    优势 - 方法在第一次失败后停止并且不会继续 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 上测试)

    【讨论】:

    • 我试图在操场上测试这个......在这一行“如果包含(src,v)==假{”。我收到错误“包含不可用:调用包含()方法关于序列"有什么想法吗..?
    【解决方案6】:

    作为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 替代品好得多。

    【讨论】:

      【解决方案7】:

      现在,我可能会使用类似的东西:

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

      ...但后来我只做了read this article,这可能使我偏向于这种解决方案。您可能可以在不让它完全不可读的情况下提高效率,但现在还早,我还没有喝咖啡。

      【讨论】:

      • 小心,如果源数组为空,这将返回true,这可能不是预期的行为。
      【解决方案8】:

      使用以下方法扩展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 大声疾呼contain 方法。

      【讨论】:

        【解决方案9】:

        以前的答案都没有似乎是对的。

        考虑:

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

        我们不会说b实际上“包含”a,但如果您的算法基于for-loop&swift的内置contains(element:)或一组,则上面的情况将通过。

        【讨论】:

          【解决方案10】:

          我自己使用这组扩展方法。我希望这段代码 sn-p 有帮助:

          
          //  Array + CommonElements.swift
          
          
          import Foundation
          
          public extension Array where Element: Hashable {
          
              func set() -> Set<Array.Element> {
                  return Set(self)
              }
          
              func isSubset(of array: Array) -> Bool {
                  self.set().isSubset(of: array.set())
              }
          
              func isSuperset(of array: Array) -> Bool {
                  self.set().isSuperset(of: array.set())
              }
          
              func commonElements(between array: Array) -> Array {
                  let intersection = self.set().intersection(array.set())
                  return intersection.map({ $0 })
              }
          
              func hasCommonElements(with array: Array) -> Bool {
                  return self.commonElements(between: array).count >= 1 ? true : false
              }
          }
          

          【讨论】:

            【解决方案11】:

            这是为 Swift 3 更新的 Maxim Shoustin's answer

            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
            }
            

            【讨论】:

              【解决方案12】:

              如果您需要确定一个数组是另一个数组的子数组。

              public extension Array where Element: Equatable {
              
                  func isSuperArray(of array: Array<Element>) -> Bool {
              
                      guard
                          count >= array.count,
                          let indexes = array.first.flatMap(indexes(of:)),
                          !indexes.isEmpty else {
                              return false
                      }
              
                      let arraysForComparison = indexes
                          .compactMap { index -> [Element]? in
                              guard index + (array.count - 1) <= count else { return nil }
                              return Array(self[index..<(index + array.count)])
                      }
              
                      return arraysForComparison.contains(array)
                  }
              
                  func isSubArray(of array: Array<Element>) -> Bool {
                      array.isSuperArray(of: self)
                  }
              
                  private func indexes(of element: Element) -> [Index] {
                      enumerated()
                          .filter { element == $0.1 }
                          .map { index, _ in index }
                  }
              }
              

              使用示例:

              let array1 = [1, 2, 3, 4]
              let array2 = [2, 3]
              
              print(array1.isSuperArray(of: array2)) // true
              print(array2.isSubArray(of: array1)) // true
              
              print(array2.isSuperArray(of: array1)) // false
              print(array1.isSubArray(of: array2)) // false
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-11-16
                • 2021-06-04
                • 1970-01-01
                • 2021-12-30
                • 2019-05-05
                相关资源
                最近更新 更多