Swift: second appearance with indexOf

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

To find the first 5 , use:

 numbers.indexOf(5) 

How to find the second appearance?

+6
source share
6 answers

You can do another index search on the rest of the array as follows:

edit / update: Xcode 9 • Swift 4 or later

 extension Collection where Element: Equatable { func secondIndex(of element: Element) -> Index? { guard let index = firstIndex(of: element) else { return nil } return self[self.index(after: index)...].firstIndex(of: element) } func indexes(of element: Element) -> [Index] { var indexes: [Index] = [] var startIndex = self.startIndex while let index = self[startIndex...].firstIndex(of: element) { indexes.append(index) startIndex = self.index(after: index) } return indexes } } 

Testing:

 let numbers = [1,3,4,5,5,9,0,1] numbers.secondIndex(of: 5) // 4 numbers.indexes(of: 5) // [3,4] 
+6
source

I do not think you can do this with indexOf . Instead, you have to use for-loop . Abridged version:

 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] 
+1
source

Here's a general extension of using Array , which will work to find the nth element in any array:

 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 
+1
source

After you find the first occurrence, you can use indexOf on the remaining fragment of the array to find the second occurrence:

 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 } 
+1
source

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) // [3, 4] indexesOf5[1] // 4 

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) // 4 [1, 3, 4, 5, 5, 9, 0, 1].nIndex(5, n: 3) // nil 

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.

0
source
 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 } } 
0
source

Source: https://habr.com/ru/post/1241343/


All Articles