斯威夫特:第二次出现的indexOf与

问题描述:

let numbers = [1,3,4,5,5,9,0,1] 

到找到的第5,用途:斯威夫特:第二次出现的indexOf与

numbers.indexOf(5) 

如何找到第二occurence?

您可以根据以下在剩下的数组切片进行了元素的索引另一个搜索的Xcode 9•斯威夫特4

extension Array where Element: Equatable { 
    func secondIndex(of element: Element) -> Index? { 
     guard let index = index(of: element) else { return nil } 
     return self[self.index(after: index)...].index(of: element) 
    } 
    func indexes(of element: Element) -> [Index] { 
     var indexes: [Index] = [] 
     var position = self.startIndex 
     while let index = self[position...].index(of: element) { 
      indexes.append(index) 
      position = self.index(after: index) 
     } 
     return indexes 
    } 
} 

测试:

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

我不认为你可以用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] 
+1

这工作太难了,因为我只想找到第二个匹配项。我的数组很大。 –

这里是一个通用的的Array SE扩展,将在任何阵列寻找一种的第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 

一旦你找到的第一次出现,你可以使用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 
} 

编辑:按@ 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<Array<Int>>的集合,该集合翻译为整数数组,返回[(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)]

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

感谢您的阅读。

P.S.在这个相对简单的解决方案中不要太冗长,但是如果上面显示的indexes方法的语法也是也是快速和肮脏,您可以在方法体中尝试类似这样的方法,其中闭包的参数针对位更清楚起见被扩展:

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

EDIT:这是一种更有效的解决方案的另一个选项,允许实施者来扫描一个特定的“索引指数”(例如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:

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 

可以看出,这种新的方法与(非常)大量的数据可能相当快;尽管如此,这有点麻烦和混乱,因此根据您的应用程序,您可能更喜欢更简单的解决方案,或者完全不同的解决方案。

(再次)感谢您的阅读。

+1

这个解决方案的问题与@Eendje的问题相同 - 如果你只想要第二次发生,你最终还是会搜索整个数组,这可能是一个巨大的性能陷阱。换句话说,在最坏的情况下,它是O(n),平均情况下,最好的情况下,而其他一些解决方案在这里最好是O(1)(如果你正在寻找第一个元素或O(2) )如果你正在寻找第二个元素),在最坏的情况下只有O(n)。 – davecom

+0

@davecom这是一个很好的观点,但在某些情况下,这不是一笔巨大的交易,并且对于少量数据,性能影响可以忽略不计。我将更新解决方案,以便更有效地找到一个特定的“索引索引”选项。 – aopsfan