【问题标题】:Swift: second occurrence with indexOfSwift:第二次出现 indexOf
【发布时间】:2016-01-24 05:48:36
【问题描述】:
let numbers = [1,3,4,5,5,9,0,1]

要查找第一个5,请使用:

numbers.indexOf(5)

如何找到第二次出现?

【问题讨论】:

    标签: swift swift3.0.2


    【解决方案1】:
    • 列表项

    您可以在剩余的数组切片处再次搜索元素的索引,如下所示:

    编辑/更新:Swift 5.2 或更高版本

    extension Collection where Element: Equatable {
        /// Returns the second index where the specified value appears in the collection.
        func secondIndex(of element: Element) -> Index? {
            guard let index = firstIndex(of: element) else { return nil }
            return self[self.index(after: index)...].firstIndex(of: element)
        }
    }
    

    extension Collection {
        /// Returns the second index in which an element of the collection satisfies the given predicate.
        func secondIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
            guard let index = try firstIndex(where: predicate) else { return nil }
            return try self[self.index(after: index)...].firstIndex(where: predicate)
        }
    }
    

    测试:

    let numbers = [1,3,4,5,5,9,0,1]
    if let index = numbers.secondIndex(of: 5) {
        print(index)    // "4\n"
    } else {
        print("not found")
    }    
    if let index = numbers.secondIndex(where: { $0.isMultiple(of: 3) }) {
        print(index)    // "5\n"
    } else {
        print("not found")
    }
    

    【讨论】:

      【解决方案2】:

      找到第一个匹配项后,您可以在数组的剩余切片上使用indexOf 来定位第二个匹配项:

      let numbers = [1,3,4,5,5,9,0,1]
      
      if let firstFive = numbers.indexOf(5)  { // 3
          let secondFive = numbers[firstFive+1..<numbers.count].indexOf(5) // 4
      }
      

      【讨论】:

        【解决方案3】:

        我认为你不能用indexOf 做到这一点。相反,您必须使用for-loop。速记版本:

        let numbers = [1,3,4,5,5,9,0,1]
        var indexes = [Int]()
        numbers.enumerate().forEach { if $0.element == 5 { indexes += [$0.index] } }
        
        print(indexes) // [3, 4]
        

        【讨论】:

        • 这太难了,因为我只想找到第二次出现。我的数组很大。
        【解决方案4】:

        这是Array 的通用扩展,可用于在任何数组中查找一种类型的第 n 个元素:

        extension Array where Element: Equatable {
            // returns nil if there is no nth occurence
            // or the index of the nth occurence if there is
            func findNthIndexOf(n: Int, thing: Element) -> Int? {
                guard n > 0 else { return nil }
                var count = 0
                for (index, item) in enumerate() where item == thing {
                    count += 1
                    if count == n {
                        return index
                    }
                }
                return nil
            }
        }
        
        let numbers = [1,3,4,5,5,9,0]
        
        numbers.findNthIndexOf(2, thing: 5) // returns 4
        

        【讨论】:

          【解决方案5】:

          编辑:根据@davecom 的评论,我在答案的底部包含了一个类似但稍微复杂一些的解决方案。

          我在这里看到了一些很好的解决方案,特别是考虑到 Swift 相对较新的语言的局限性。也有一种非常简洁的方法,但要注意......它相当快速和肮脏。可能不是完美的解决方案,但它非常快。也非常多才多艺(不是吹牛)。

          extension Array where Element: Equatable {
              func indexes(search: Element) -> [Int] {
                  return enumerate().reduce([Int]()) { $1.1 == search ? $0 + [$1.0] : $0 }
              }
          }
          

          使用此扩展,您可以按如下方式访问第二个索引:

          let numbers = [1, 3, 4, 5, 5, 9, 0, 1]
          let indexesOf5 = numbers.indexes(5) // [3, 4]
          
          indexesOf5[1] // 4
          

          你就完成了!

          基本上,该方法的工作方式如下:enumerate() 将数组映射到元组,包括每个元素的索引和元素本身。在这种情况下,[1, 3, 4, 5, 5, 9, 0, 1].enumerate() 返回一个 EnumerateSequence&lt;Array&lt;Int&gt;&gt; 类型的集合,将其转换为整数数组,返回 [(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)]

          其余的工作是使用reduce(在某些语言中称为“注入”)完成的,这是许多编码人员不熟悉的非常强大的工具。如果读者是这些编码人员中的一员,我建议查看this article 关于在 JS 中使用函数的信息(请记住,传入的非块参数的位置是在 JS 中的块之后输入的,而不是之前作为见这里)。

          感谢阅读。

          附:在这个相对简单的解决方案上不要太啰嗦,但是如果上面显示的 indexes 方法的语法有点too 快速和肮脏,你可以在方法体,其中闭包的参数被扩展为更清晰:

          return enumerate().reduce([Int]()) { memo, element in
              element.1 == search ? memo + [element.0] : memo
          }
          

          编辑:这是另一个选项,允许实施者扫描特定的“索引处的索引”(例如第二次出现 5)以获得更有效的解决方案。

          extension Array where Element: Equatable {
              func nIndex(search: Element, n: Int) -> Int? {
                  let info = enumerate().reduce((count: 0, index: 0), combine: { memo, element in
                      memo.count < n && element.1 == search ? (count: memo.count + 1, index: element.0) : memo
                  })
                  return info.count == n ? info.index : nil
              }
          }
          
          [1, 3, 4, 5, 5, 9, 0, 1].nIndex(5, n: 2) // 4
          [1, 3, 4, 5, 5, 9, 0, 1].nIndex(5, n: 3) // nil
          

          新方法仍然遍历整个数组,但由于之前的方法中缺少“数组构建”,因此效率更高。对于大多数使用的 8 对象数组,这种性能影响可以忽略不计。但是考虑一下从 0 到 99 的 10,000 个随机数的列表:

          let randomNumbers = (1...10000).map{_ in Int(rand() % 100)}
          
          let indexes = randomNumbers.indexes(93) // count -> 100 (in my first run)
          let index1 = indexes[1] // 238
          // executed in 29.6603130102158 sec
          
          let index2 = randomNumbers.nIndex(93, n: 2) // 238
          // executed in 3.82625496387482 sec
          

          可以看出,这种新方法对于(非常)大的数据集要快得多;虽然它有点麻烦和混乱,所以根据您的应用程序,您可能更喜欢更简单的解决方案,或者完全不同的解决方案。

          (再次)感谢您的阅读。

          【讨论】:

          • 此解决方案的问题与@Eendje 的问题相同 - 如果您只想要第二次出现,您最终仍将搜索整个数组,这可能是一个巨大的性能缺陷。换句话说,在最坏情况、平均情况和最佳情况下它是 O(n),而这里的其他几个解决方案在最好情况下是 O(1)(如果你正在寻找第一个元素或 O(2 ) 如果您正在寻找第二个元素),并且在最坏的情况下只有 O(n)。
          • @davecom 这是一个很好的观点,但在某些情况下,这并不是什么大问题,而且在少量数据的情况下对性能的影响可以忽略不计。我将更新解决方案以包含一个选项以更有效地查找特定的“索引处的索引”。
          【解决方案6】:
          extension Collection where Element: Equatable {
              func nth(occurance: Int, of element: Element) -> Index? {
                  var level : Int = occurance
                  var position = self.startIndex
                  while let index = self[position...].index(of: element) {
                      level -= 1
                      guard level >= 0 else { return nil }
                      guard level != 0 else { return index }
                      position = self.index(after: index)
                  }
                  return nil
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-07-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-04-06
            • 1970-01-01
            相关资源
            最近更新 更多