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

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

numbers.indexOf(5)

我如何找到第二次出现?

4

6 回答 6

9
  • 项目清单

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

编辑/更新: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")
}
于 2016-01-24T06:13:23.617 回答
3

找到第一个匹配项后,您可以使用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
}
于 2016-01-24T08:30:44.557 回答
2

这是一个通用扩展,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
于 2016-01-24T06:49:26.807 回答
2

我不认为你可以做到这一点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]
于 2016-01-24T06:00:14.603 回答
1

编辑:根据@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(在某些语言中称为“注入”)完成的,这是许多编码人员不熟悉的极其强大的工具。如果读者是这些编码人员中的一员,我建议查看这篇关于在 JS 中使用函数的文章(请记住,传入的非块参数的位置是在 JS 中的块之后输入的,而不是之前所见这里)。

谢谢阅读。

PS 不要对这个相对简单的解决方案太啰嗦,但是如果indexes上面显示的方法的语法有点太快和太脏,你可以在方法体中尝试这样的东西,其中闭包的参数被扩展为了更清楚一点:

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

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

(再次)感谢您的阅读。

于 2016-01-24T08:43:05.903 回答
0
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
    }
}
于 2018-05-18T18:49:56.047 回答