【问题标题】:How to iterate a loop with index and element in Swift如何在 Swift 中使用索引和元素迭代循环
【发布时间】:2014-07-24 13:42:35
【问题描述】:

是否有一个函数可以用来迭代数组并同时具有索引和元素,例如 Python 的 enumerate

for index, element in enumerate(list):
    ...

【问题讨论】:

    标签: arrays swift enumeration


    【解决方案1】:

    是的。从 Swift 3.0 开始,如果您需要每个元素的索引及其值,您可以使用 enumerated() method 来遍历数组。它返回由数组中每个项目的索引和值组成的对序列。例如:

    for (index, element) in list.enumerated() {
      print("Item \(index): \(element)")
    }
    

    在 Swift 3.0 之前和 Swift 2.0 之后,该函数被称为enumerate()

    for (index, element) in list.enumerate() {
        print("Item \(index): \(element)")
    }
    

    在 Swift 2.0 之前,enumerate 是一个全局函数。

    for (index, element) in enumerate(list) {
        println("Item \(index): \(element)")
    }
    

    【讨论】:

    • 虽然它看起来像一个元组,但在 Swift 1.2 - 不确定 2.0 - enumerate 返回一个 EnumerateSequence 结构。
    • @Leviathlon 显着或可衡量的性能开销是否重要?没有。
    • 也许他们会换成动名词,enumerating 代表 Swift 4。令人兴奋!
    • @Honey for (index, element) in 在使用 enumerated 时具有误导性。应该是for (offset, element) in
    • 我们是否举行抽奖活动,谁能猜出接下来会发生什么变化。这种该死的语言简直不能坐以待毙,哈哈
    【解决方案2】:

    Swift 5 为Array 提供了一个名为enumerated() 的方法。 enumerated() 有以下声明:

    func enumerated() -> EnumeratedSequence<Array<Element>>
    

    返回对 (n, x) 的序列,其中 n 表示从零开始的连续整数,x 表示序列的一个元素。


    在最简单的情况下,您可以将 enumerated() 与 for 循环一起使用。例如:

    let list = ["Car", "Bike", "Plane", "Boat"]
    for (index, element) in list.enumerated() {
        print(index, ":", element)
    }
    
    /*
    prints:
    0 : Car
    1 : Bike
    2 : Plane
    3 : Boat
    */
    

    但请注意,您不限于将 enumerated() 与 for 循环一起使用。事实上,如果你打算将 enumerated() 与 for 循环一起用于类似于以下代码的内容,那么你做错了:

    let list = [Int](1...5)
    var arrayOfTuples = [(Int, Int)]()
    
    for (index, element) in list.enumerated() {
        arrayOfTuples += [(index, element)]
    }
    
    print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
    

    更快捷的方法是:

    let list = [Int](1...5)
    let arrayOfTuples = Array(list.enumerated())
    print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]
    

    您也可以将enumerated()map 一起使用:

    let list = [Int](1...5)
    let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
    print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]
    

    此外,虽然它有一些limitationsforEach 可以很好地替代 for 循环:

    let list = [Int](1...5)
    list.reversed().enumerated().forEach { print($0, ":", $1) }
    
    /*
    prints:
    0 : 5
    1 : 4
    2 : 3
    3 : 2
    4 : 1
    */
    

    通过使用enumerated()makeIterator(),您甚至可以手动迭代您的Array。例如:

    import UIKit
    import PlaygroundSupport
    
    class ViewController: UIViewController {
    
        var generator = ["Car", "Bike", "Plane", "Boat"].enumerated().makeIterator()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let button = UIButton(type: .system)
            button.setTitle("Tap", for: .normal)
            button.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
            button.addTarget(self, action: #selector(iterate(_:)), for: .touchUpInside)
            view.addSubview(button)
        }
    
        @objc func iterate(_ sender: UIButton) {
            let tuple = generator.next()
            print(String(describing: tuple))
        }
    
    }
    
    PlaygroundPage.current.liveView = ViewController()
    
    /*
     Optional((offset: 0, element: "Car"))
     Optional((offset: 1, element: "Bike"))
     Optional((offset: 2, element: "Plane"))
     Optional((offset: 3, element: "Boat"))
     nil
     nil
     nil
     */
    

    【讨论】:

    • 访问索引是使用enumerate的唯一好处吗?
    【解决方案3】:

    从 Swift 2 开始,需要像这样在集合上调用 enumerate 函数:

    for (index, element) in list.enumerate() {
        print("Item \(index): \(element)")
    }
    

    【讨论】:

      【解决方案4】:

      我在寻找使用字典的方法时找到了这个答案,结果证明它很容易适应它,只需为元素传递一个元组。

      // Swift 2
      
      var list = ["a": 1, "b": 2]
      
      for (index, (letter, value)) in list.enumerate() {
          print("Item \(index): \(letter) \(value)")
      }
      

      【讨论】:

        【解决方案5】:

        为了完整起见,您可以简单地遍历数组索引并使用下标访问相应索引处的元素:

        let list = [100,200,300,400,500]
        for index in list.indices {
            print("Element at:", index, " Value:", list[index])
        }
        

        使用 forEach

        list.indices.forEach {
            print("Element at:", $0, " Value:", list[$0])
        }
        

        使用集合enumerated() 方法。请注意,它返回带有offsetelement 的元组集合:

        for item in list.enumerated() {
            print("Element at:", item.offset, " Value:", item.element)
        }
        

        使用 forEach:

        list.enumerated().forEach {
            print("Element at:", $0.offset, " Value:", $0.element)
        }
        

        那些会打印出来的

        元素位于:0 值:100

        元素位于:1 值:200

        元素位于:2 值:300

        元素位于:3 值:400

        元素在:4 值:500

        如果您需要数组索引(不是偏移量)及其元素,您可以扩展 Collection 并创建自己的方法来获取索引元素:

        extension Collection {
            func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
                var index = startIndex
                for element in self {
                    try body((index,element))
                    formIndex(after: &index)
                }
            }
        }
        

        Alex 建议的另一种可能的实现是使用其元素压缩集合索引:

        extension Collection {
            func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
                for element in zip(indices, self) { try body(element) }
            }
            var indexedElements: Zip2Sequence<Indices, Self> { zip(indices, self) }
        }
        

        测试:

        let list =  ["100","200","300","400","500"]
        
        // You can iterate the index and its elements using a closure
        list.dropFirst(2).indexedElements {
            print("Index:", $0.index, "Element:", $0.element)
        }
        
        // or using a for loop
        for (index, element) in list.indexedElements  {
            print("Index:", index, "Element:", element)
        }
        

        这将打印[打印

        索引:2 元素:300

        索引:3 元素:400

        索引:4 元素:500

        索引:0 元素:100

        索引:1 元素:200

        索引:2 元素:300

        索引:3 元素:400

        索引:4 元素:500

        【讨论】:

        • 顺便说一句,您可以通过循环使用zip(self.indices, self) 来实现enumeratedIndices
        • @Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }。顺便说一句,我不喜欢我选择的命名,indexedElements 可能会更好地描述它的作用
        • 噢,我认为这是一个更好的名字。是的,for 循环有效,而且zip(self.indices, self) .forEach(body)
        • @Alexander-ReinstateMonica forEach 在幕后执行 for 循环。我更喜欢保持简单 github.com/apple/swift/blob/master/stdlib/public/core/… @inlinable public func forEach( _ body: (Element) throws -&gt; Void ) rethrows { for element in self { try body(element) } } }
        【解决方案6】:

        斯威夫特 5.x:

        我个人更喜欢使用forEach 方法:

        list.enumerated().forEach { (index, element) in
            ...
        }
        

        你也可以使用短版:

        list.enumerated().forEach { print("index: \($0.0), value: \($0.1)") }
        

        【讨论】:

        • forEach 的有趣花絮无法返回值 - void 函数中出现意外的非 void 返回值。所以之后返回结果。
        【解决方案7】:

        您可以简单地使用枚举循环来获得您想要的结果:

        斯威夫特 2:

        for (index, element) in elements.enumerate() {
            print("\(index): \(element)")
        }
        

        Swift 3 和 4:

        for (index, element) in elements.enumerated() {
            print("\(index): \(element)")
        }
        

        或者您可以简单地通过一个 for 循环来获得相同的结果:

        for index in 0..<elements.count {
            let element = elements[index]
            print("\(index): \(element)")
        }
        

        希望对你有帮助。

        【讨论】:

          【解决方案8】:

          基本枚举

          for (index, element) in arrayOfValues.enumerate() {
          // do something useful
          }
          

          或使用 Swift 3...

          for (index, element) in arrayOfValues.enumerated() {
          // do something useful
          }
          

          枚举、过滤和映射

          不过,我最常将 enumerate 与 map 或 filter 结合使用。例如对几个数组进行操作。

          在这个数组中,我想过滤奇数或偶数索引元素并将它们从 Ints 转换为 Doubles。所以enumerate() 获取索引和元素,然后过滤器检查索引,最后为了摆脱结果元组,我将它映射到元素。

          let evens = arrayOfValues.enumerate().filter({
                                      (index: Int, element: Int) -> Bool in
                                      return index % 2 == 0
                                  }).map({ (_: Int, element: Int) -> Double in
                                      return Double(element)
                                  })
          let odds = arrayOfValues.enumerate().filter({
                                      (index: Int, element: Int) -> Bool in
                                      return index % 2 != 0
                                  }).map({ (_: Int, element: Int) -> Double in
                                      return Double(element)
                                  })
          

          【讨论】:

            【解决方案9】:

            Swift 5.x:

            让列表 = [0, 1, 2, 3, 4, 5]

            list.enumerated().forEach { (index, value) in
                print("index: \(index), value: \(value)")
            }
            

            或者,

            list.enumerated().forEach { 
                print("index: \($0.offset), value: \($0.element)")
            } 
            

            或者,

            for (index, value) in list.enumerated() {
                print("index: \(index), value: \(value)")
            }
            

            【讨论】:

              【解决方案10】:

              使用.enumerate() 有效,但它不提供元素的真实索引;它只提供一个以 0 开头并为每个连续元素递增 1 的 Int。这通常无关紧要,但与 ArraySlice 类型一起使用时可能会出现意外行为。取以下代码:

              let a = ["a", "b", "c", "d", "e"]
              a.indices //=> 0..<5
              
              let aSlice = a[1..<4] //=> ArraySlice with ["b", "c", "d"]
              aSlice.indices //=> 1..<4
              
              var test = [Int: String]()
              for (index, element) in aSlice.enumerate() {
                  test[index] = element
              }
              test //=> [0: "b", 1: "c", 2: "d"] // indices presented as 0..<3, but they are actually 1..<4
              test[0] == aSlice[0] // ERROR: out of bounds
              

              这是一个有点人为的例子,在实践中这不是一个常见的问题,但我仍然认为值得知道这可能发生。

              【讨论】:

              • it does not actually provide the true index of the element; it only provides an Int beginning with 0 and incrementing by 1 for each successive element 是的,这就是为什么它被称为 enumerate。此外, slice 不是数组,因此它的行为不同也就不足为奇了。这里没有错误 - 一切都是设计使然。 :)
              • 没错,但我从未称其为错误。对于那些不知道它如何与 ArraySlice 类型产生负面交互的人来说,我认为这是值得一提的潜在意外行为。
              • 您是否知道任何获取实际元素索引的方法 - 例如,如果首先使用filter
              【解决方案11】:

              从 Swift 3 开始,就是

              for (index, element) in list.enumerated() {
                print("Item \(index): \(element)")
              }
              

              【讨论】:

                【解决方案12】:

                这是枚举循环的公式:

                for (index, value) in shoppingList.enumerate() {
                print("Item \(index + 1): \(value)")
                }
                

                更多详情您可以查看Here

                【讨论】:

                  【解决方案13】:

                  对于那些想要使用forEach的人。

                  斯威夫特 4

                  extension Array {
                    func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
                      try zip((startIndex ..< endIndex), self).forEach(body)
                    }
                  }
                  

                  或者

                  array.enumerated().forEach { ... }
                  

                  【讨论】:

                    【解决方案14】:

                    Xcode 8 和 Swift 3: 可以使用tempArray.enumerated()枚举数组

                    例子:

                    var someStrs = [String]()
                    
                    someStrs.append("Apple")  
                    someStrs.append("Amazon")  
                    someStrs += ["Google"]    
                    
                    
                    for (index, item) in someStrs.enumerated()  
                    {  
                            print("Value at index = \(index) is \(item)").  
                    }
                    

                    控制台:

                    Value at index = 0 is Apple
                    Value at index = 1 is Amazon
                    Value at index = 2 is Google
                    

                    【讨论】:

                      【解决方案15】:

                      对于您想要做的事情,您应该在 Array 上使用 enumerated() 方法:

                      for (index, element) in list.enumerated() {
                          print("\(index) - \(element)")
                      }
                      

                      【讨论】:

                      • enumerated() 是我一直在寻找的魔法咒语......它修复了我的“uple pattern cannot match values of non-tuple type 'Items'”错误......谢谢!
                      【解决方案16】:

                      在函数式编程中像这样使用 .enumerated():

                      list.enumerated().forEach { print($0.offset, $0.element) } 
                      

                      【讨论】:

                        【解决方案17】:

                        我们调用 enumerate 函数来实现这一点。喜欢

                            for (index, element) in array.enumerate() {
                             index is indexposition of array
                             element is element of array 
                           }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-01-03
                          • 2019-07-28
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多