【问题标题】:check if all elements of an array have the same value in Swift检查数组的所有元素在 Swift 中是否具有相同的值
【发布时间】:2015-04-12 09:56:57
【问题描述】:

在 Swift 中是否有一个函数可以检查数组的所有元素是否具有相同的值?在我的例子中,它是一个Int 类型的数组。我知道我可以使用一个简单的 for 循环对其进行迭代我只是想知道是否有内置的更快的东西。

【问题讨论】:

    标签: arrays swift


    【解决方案1】:

    使用 Swift 5,您可以使用以下四种方法之一来测试数组的所有元素是否相等。


    #1。使用ArrayallSatisfy(_:)方法

    allSatisfy(_:) 返回一个布尔值,指示序列的每个元素是否满足给定的谓词。您可以设置谓词来测试数组的所有元素是否相等:

    let array = [1, 1, 1]
    
    let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
    print(hasAllItemsEqual) // prints: true
    
    let array = [1, 1, 3]
    
    let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
    print(hasAllItemsEqual) // prints: false
    
    let array = [Int]()
    
    let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
    print(hasAllItemsEqual) // prints: true
    

    #2。使用Arrayreduce(_:_:)方法

    作为allSatisfy(_:) 的替代品,您可以使用reduce(_:_:)

    let array = [1, 1, 1]
    
    let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
        return partialResult && element == array.first
    }
    print(hasAllItemsEqual) // prints: true
    
    let array = [1, 1, 3]
    
    let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
        return partialResult && element == array.first
    }
    print(hasAllItemsEqual) // prints: false
    
    let array = [Int]()
    
    let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
        return partialResult && element == array.first
    }
    print(hasAllItemsEqual) // prints: true
    

    #3。使用elementsEqual(_:)方法

    elementsEqual(_:) 返回一个布尔值,指示两个序列是否包含相同顺序的相同元素。因此,您可以通过重复初始数组的第一个元素并将前者与后者进行比较来创建一个新集合:

    let array = [1, 1, 1]
    
    precondition(!array.isEmpty)
    let repeated = repeatElement(array[0], count: array.count)
    
    let hasAllItemsEqual = array.elementsEqual(repeated)
    print(hasAllItemsEqual) // prints: true
    
    let array = [1, 1, 3]
    
    precondition(!array.isEmpty)
    let repeated = repeatElement(array[0], count: array.count)
    
    let hasAllItemsEqual = array.elementsEqual(repeated)
    print(hasAllItemsEqual) // prints: false
    

    #4。使用 Setinit(_:) 初始化器

    如果一个数组的所有元素都是相等的,从这个数组创建一个集合应该导致这个集合只有一个元素:

    let array = [1, 1, 1]
    
    let set = Set(array)
    let hasAllItemsEqual = set.count <= 1
    print(hasAllItemsEqual) // prints: true
    
    let array = [1, 1, 3]
    
    let set = Set(array)
    let hasAllItemsEqual = set.count <= 1
    print(hasAllItemsEqual) // prints: false
    
    let array = [Int]()
    
    let set = Set(array)
    let hasAllItemsEqual = set.count <= 1
    print(hasAllItemsEqual) // prints: true
    

    【讨论】:

      【解决方案2】:

      任何方法都必须遍历所有元素,直到找到不同的元素:

      func allEqualUsingLoop<T : Equatable>(array : [T]) -> Bool {
          if let firstElem = array.first {
              for elem in array {
                  if elem != firstElem {
                      return false
                  }
              }
          }
          return true
      }
      

      您可以使用contains() 函数代替显式循环:

      func allEqualUsingContains<T : Equatable>(array : [T]) -> Bool {
          if let firstElem = array.first {
              return !contains(array, { $0 != firstElem })
          }
          return true
      }
      

      如果数组元素是Hashable(比如Int)那么你可以 从数组元素创建一个Set(从 Swift 1.2 开始可用)并检查它是否只有一个元素。

      func allEqualUsingSet<T : Hashable>(array : [T]) -> Bool {
          let uniqueElements = Set(array)
          return count(uniqueElements) <= 1
      }
      

      快速基准测试表明,“包含”方法比“设置”方法快得多 对于 1,000,000 个整数的数组,特别是如果元素是 都相等。这是有道理的,因为contains() 会尽快返回 作为一个不匹配的元素被发现,而Set(array)总是 遍历整个数组。

      “包含”方法也比显式循环同样快或略快。

      这是一些简单的基准测试代码。当然结果可能会有所不同 与数组大小、不同元素的数量和元素数据类型有关。

      func measureExecutionTime<T>(title: String,  @noescape f : (() -> T) ) -> T {
          let start = NSDate()
          let result = f()
          let end = NSDate()
          let duration = end.timeIntervalSinceDate(start)
          println("\(title) \(duration)")
          return result
      }
      
      var array = [Int](count: 1_000_000, repeatedValue: 1)
      array[500_000] = 2
      
      let b1 = measureExecutionTime("using loop    ") {
          return allEqualUsingLoop(array)
      }
      
      let b2 = measureExecutionTime("using contains") {
          allEqualUsingContains(array)
      }
      
      let b3 = measureExecutionTime("using set     ") {
          allEqualUsingSet(array)
      }
      

      结果(在 MacBook Pro 上,发布配置):

      使用循环 0.000651001930236816 使用包含 0.000567018985748291 使用集合 0.0344770550727844

      array[1_000] = 2 的结果是

      使用循环 9.00030136108398e-06 使用包含 2.02655792236328e-06 使用集合 0.0306439995765686

      Swift 2/Xcode 7 更新:由于 Swift 中的各种变化 语法,函数现在写成

      func allEqual<T : Equatable>(array : [T]) -> Bool {
          if let firstElem = array.first {
              return !array.dropFirst().contains { $0 != firstElem }
          }
          return true
      }
      

      但您现在也可以将其定义为数组的扩展方法:

      extension Array where Element : Equatable {
          func allEqual() -> Bool {
              if let firstElem = first {
                  return !dropFirst().contains { $0 != firstElem }
              }
              return true
          }
      }
      
      print([1, 1, 1].allEqual()) // true
      print([1, 2, 1].allEqual()) // false
      

      【讨论】:

      • 正在尝试相同的...编译器已检查 ;-)
      • 如果你有兴趣尝试另一个,你可以使用equalRepeatarray.first.map { equal(array, Repeat(count: array.count, repeatedValue: $0)) } ?? true(速度较慢)
      • @AirspeedVelocity:速度较慢,但​​并不剧烈:array[500_000] = 2 测量为 0.001789 秒,array[1_000] = 2 测量为 3.99e-06 秒。
      • 是的,我希望它检查它们的长度是否相同(即使这实际上并不重要),这将是额外的开销。
      • 这是一个很好的答案!谢谢
      【解决方案3】:

      Swift 4.2/Xcode 10 的解决方案:

      let arr = [1, 1, 1, 1]
      let allItemsEqual = arr.dropLast().allSatisfy { $0 == arr.last }
      print(allItemsEqual)
      

      如果您当前的 Xcode 版本低于 10.0,您可以在 Xcode9to10Preparation 中找到 ArraySlice 的函数 allSatisfy。你可以用 CocoaPods 安装这个库。

      【讨论】:

      • 你真的不需要dropLast()
      • @Sulthan 在此处添加dropLast() 很重要。在我的示例中,如果我将删除 dropLast() 传递给 allSatisfy 的块将被调用 4 次,但现在它只调用了 3 次,结果是一样的。
      • 你不需要它。复杂度是一样的
      • @Lirik 你可以添加你自己的答案并解释为什么不应该有dropLast()
      • 我理解检查最后一个对象是否等于自身是多余的,但是遍历数组 3 次或 4 次在 CS 术语中具有相同的复杂性,所以这并不重要。跨度>
      猜你喜欢
      • 2014-01-03
      • 1970-01-01
      • 2012-05-20
      • 1970-01-01
      • 2011-01-19
      • 2012-05-04
      • 1970-01-01
      • 2018-05-13
      • 1970-01-01
      相关资源
      最近更新 更多