Here's a sample model I came up with for testing:
struct S {
let id: Int
let depth: Int
}
var id = 0
let getID: () -> Int = { defer { id += 1 }; return id }
let objects = [
S(id: getID(), depth: 1),
S(id: getID(), depth: 3),
S(id: getID(), depth: 2),
S(id: getID(), depth: 3),
S(id: getID(), depth: 4),
]
Here's a solution that account for the situations in which there are no elements which match the predicate, or only 1 such element:
let isDepth3: (S) -> Bool = { $0.depth == 3 }
// Get the index of the first item (can be nil)
let indexOfFirstDepth3 = objects.index(where: isDepth3)
// Get the index after that (can be nil), so that we can exclude everything before it
let firstIndexOfRemainingItems = indexOfFirstDepth3.flatMap { objects.index($0, offsetBy: +1, limitedBy: objects.endIndex) }
let indexOfSecondDepth3 = firstIndexOfRemainingItems.flatMap {
// Slice the `objects` array, to omit all the items before up to and including the first depth 3 item.
// Then find the index of the next next 3 item thereafter.
return objects[$0...].index(where: isDepth3)
}
// Print results
func stringifyOptional<T>(_ item: T?) -> String {
return item.map{ String(describing: $0) } ?? "nil"
}
print("First item with depth 3 is \(stringifyOptional(indexOfFirstDepth3.map{ objects[$0] })) at index \(stringifyOptional(indexOfFirstDepth3))")
print("Second item with depth 3 is \(stringifyOptional(indexOfSecondDepth3.map{ objects[$0] })) at index \(stringifyOptional(indexOfFirstDepth3))")
If you're sure that you'll have 2 such elements, and you're sure that force unwrapping will be safe, then this can be simplified dramatically:
let isDepth3: (S) -> Bool = { $0.depth == 3 }
let indexOfFirstDepth3 = objects.index(where: isDepth3)!
let indexOfSecondDepth3 = objects[indexOfFirstDepth3...].index(where: isDepth3)!
// Just printing the result
print("First item with depth 3 is \(objects[indexOfFirstDepth3]) at index \(indexOfFirstDepth3)")
print("Second item with depth 3 is \(objects[indexOfFirstDepth3])) at index \(indexOfFirstDepth3)")