Swift: 第二次出现 indexOf
Swift: second occurrence with indexOf
let numbers = [1,3,4,5,5,9,0,1]
要查找第一个 5
,请使用:
numbers.indexOf(5)
如何找到第二次出现的地方?
我认为 indexOf
做不到。相反,您必须使用 for-loop
。 shorthand 版本:
let numbers = [1,3,4,5,5,9,0,1]
var indexes = [Int]()
numbers.enumerate().forEach { if [=10=].element == 5 { indexes += [[=10=].index] } }
print(indexes) // [3, 4]
- 列表项
您可以再次搜索剩余数组切片中的元素索引,如下所示:
edit/update: 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: { [=12=].isMultiple(of: 3) }) {
print(index) // "5\n"
} else {
print("not found")
}
下面是 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
找到第一个匹配项后,您可以在数组的剩余切片上使用 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 相对较新的语言的局限性。也有一种非常简洁的方法,但要注意……它是 quick-and-dirty。可能不是完美的解决方案,但它非常快。还非常多才多艺(不是吹牛)。
extension Array where Element: Equatable {
func indexes(search: Element) -> [Int] {
return enumerate().reduce([Int]()) { .1 == search ? [=10=] + [.0] : [=10=] }
}
}
使用此扩展,您可以按如下方式访问第二个索引:
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()
returns 类型 EnumerateSequence<Array<Int>>
的集合,转换为整数数组,returns [(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)]
.
其余的工作是使用 reduce
(在某些语言中称为 'inject')完成的,这是一个极其的强大工具,许多编码人员都在使用它不熟悉。如果 reader 在这些编码器中,我建议检查 this article 关于在 JS 中使用该函数的信息(请记住,传入的 non-block 参数的位置是在在 JS 中阻止,而不是像这里看到的那样之前)。
感谢阅读。
P.S。在这个相对简单的解决方案上不要太 long-winded,但是如果上面显示的 indexes
方法的语法有点 太 quick-and-dirty,你可以在方法体中尝试这样的事情,为了更清楚一点,闭包的参数被扩展了:
return enumerate().reduce([Int]()) { memo, element in
element.1 == search ? memo + [element.0] : memo
}
EDIT:这是另一个选项,允许实施者扫描特定的 "index at index"(例如第二次出现 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
新方法仍然遍历整个数组,但由于缺少以前方法中的 "array-building",因此效率更高。对于大多数使用的 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
可以看出,对于(非常)大的数据集,这种新方法要快得多;虽然它有点麻烦和混乱,因此根据您的应用程序,您可能更喜欢更简单的解决方案,或者完全不同的解决方案。
(再次)感谢阅读
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
}
}
let numbers = [1,3,4,5,5,9,0,1]
要查找第一个 5
,请使用:
numbers.indexOf(5)
如何找到第二次出现的地方?
我认为 indexOf
做不到。相反,您必须使用 for-loop
。 shorthand 版本:
let numbers = [1,3,4,5,5,9,0,1]
var indexes = [Int]()
numbers.enumerate().forEach { if [=10=].element == 5 { indexes += [[=10=].index] } }
print(indexes) // [3, 4]
- 列表项
您可以再次搜索剩余数组切片中的元素索引,如下所示:
edit/update: 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: { [=12=].isMultiple(of: 3) }) {
print(index) // "5\n"
} else {
print("not found")
}
下面是 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
找到第一个匹配项后,您可以在数组的剩余切片上使用 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 相对较新的语言的局限性。也有一种非常简洁的方法,但要注意……它是 quick-and-dirty。可能不是完美的解决方案,但它非常快。还非常多才多艺(不是吹牛)。
extension Array where Element: Equatable {
func indexes(search: Element) -> [Int] {
return enumerate().reduce([Int]()) { .1 == search ? [=10=] + [.0] : [=10=] }
}
}
使用此扩展,您可以按如下方式访问第二个索引:
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()
returns 类型 EnumerateSequence<Array<Int>>
的集合,转换为整数数组,returns [(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)]
.
其余的工作是使用 reduce
(在某些语言中称为 'inject')完成的,这是一个极其的强大工具,许多编码人员都在使用它不熟悉。如果 reader 在这些编码器中,我建议检查 this article 关于在 JS 中使用该函数的信息(请记住,传入的 non-block 参数的位置是在在 JS 中阻止,而不是像这里看到的那样之前)。
感谢阅读。
P.S。在这个相对简单的解决方案上不要太 long-winded,但是如果上面显示的 indexes
方法的语法有点 太 quick-and-dirty,你可以在方法体中尝试这样的事情,为了更清楚一点,闭包的参数被扩展了:
return enumerate().reduce([Int]()) { memo, element in
element.1 == search ? memo + [element.0] : memo
}
EDIT:这是另一个选项,允许实施者扫描特定的 "index at index"(例如第二次出现 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
新方法仍然遍历整个数组,但由于缺少以前方法中的 "array-building",因此效率更高。对于大多数使用的 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
可以看出,对于(非常)大的数据集,这种新方法要快得多;虽然它有点麻烦和混乱,因此根据您的应用程序,您可能更喜欢更简单的解决方案,或者完全不同的解决方案。
(再次)感谢阅读
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
}
}