EDIT : as @davecom commented, I included a similar, but slightly more complex solution at the bottom of the answer.
Here I see a couple of good solutions, especially considering the limitations regarding the new Swift language. There is a very concise way to do this, but be careful ... it's pretty quick and dirty. It may not be the perfect solution, but it is pretty fast. Also very versatile (don't brag).
extension Array where Element: Equatable { func indexes(search: Element) -> [Int] { return enumerate().reduce([Int]()) { $1.1 == search ? $0 + [$1.0] : $0 } } }
Using this extension, you can access the second index as follows:
let numbers = [1, 3, 4, 5, 5, 9, 0, 1] let indexesOf5 = numbers.indexes(5)
And you're done!
In principle, the method works as follows: enumerate() maps the array to tuples, including the index of each element with the element itself. In this case, [1, 3, 4, 5, 5, 9, 0, 1].enumerate() returns a collection of type EnumerateSequence<Array<Int>> , which, translated into the Integer array, returns [(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)] .
The rest of the work is done using reduce (called "injection" in some languages), which is a powerful tool extremely that many coders are not familiar with. If the reader is one of these encoders, I would recommend checking out this article regarding function use in JS (remember to place an unblocked argument after the block in JS, not before, as shown here).
Thank you for reading.
PS is not too long regarding this relatively simple solution, but if the syntax for the indexes method shown above is too fast and dirty, you can try something like this in the body of the method, where the closing parameters are extended for clarity:
return enumerate().reduce([Int]()) { memo, element in element.1 == search ? memo + [element.0] : memo }
EDIT : there is another option here that allows the developer to scan a specific index with an index (for example, second occurrence 5) for a more efficient solution.
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)
The new method still iterates over the entire array, but is much more efficient due to the lack of "building an array" in the previous method. This performance hit would be negligible with the array of 8 objects used for most. But consider a list of 10,000 random numbers from 0 to 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
As you can see, this new method is much faster with a (very) large data set; it's a little more cumbersome and confusing, so depending on your application, you might prefer a simpler solution or something else entirely.
(Again) thanks for reading.