Swift:indexOf的第二次出现

时间:2016-01-24 05:48:37

标签: swift swift3.0.2

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

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

numbers.indexOf(5)

如何找到第二次出现?

6 个答案:

答案 0 :(得分:5)

您可以在剩余的数组切片上执行另一个元素索引搜索,如下所示:

编辑/更新: Xcode 9•Swift 4

extension Collection 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 startIndex = self.startIndex
        while let index = self[startIndex...].index(of: element) {
            indexes.append(index)
            startIndex = 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]

答案 1 :(得分:1)

我认为你不能用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]

答案 2 :(得分:1)

这里是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

答案 3 :(得分:1)

找到第一个匹配项后,可以在数组的剩余切片上使用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
}

答案 4 :(得分:0)

编辑:根据@ 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
}

编辑:这是另一个选项,允许实施者扫描特定的“索引索引”(例如第二次出现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

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

(再次)感谢阅读。

答案 5 :(得分: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
    }
}