斯威夫特:第二次出现的indexOf与
您可以根据以下在剩下的数组切片进行了元素的索引另一个搜索的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]
这里是一个通用的的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
可以看出,这种新的方法与(非常)大量的数据可能相当快;尽管如此,这有点麻烦和混乱,因此根据您的应用程序,您可能更喜欢更简单的解决方案,或者完全不同的解决方案。
(再次)感谢您的阅读。
这工作太难了,因为我只想找到第二个匹配项。我的数组很大。 –