Search code examples
swiftindexingrangevariable-types

ClosedRange<Int>.Index to Int in Swift?


My code:

let range = 30...200 //ranges may have different bounds
let index = range.firstIndex(of: 55)

The problem is index has type ClosedRange<Int>.Index and it is unclear how to convert it to Int now for further usage.

I understand that it is simplier to convert range to array and look for int index in it but they somehow should allow to use ClosedRange<Int>.Index by itself

Note: there are may be similar questions but all of them about how to convert ClosedRange<Int> to an array, not index conversion


Solution

  • ClosedRange<Bound>.Index is literally just an enum that doesn't tell you anything useful.

    enum Index {
        case inRange(Bound)
        case pastEnd
    }
    

    Something as simple as this is enough to correctly implement the index-related methods that Collection and other protocols require.

    firstIndex(of: 55) would return inRange(55) if 55 is in the range, and pastEnd otherwise.

    Also note that firstIndex (and other Collection methods) is only available when Bound conforms to Stridable and the Stride conforms to SignedInteger. After all, it doesn't make sense to treat a ClosedRange<Double> or a ClosedRange<String> as a Collection.

    So the only ranges where an operation like firstIndex(of: 55) makes sense, is when Bound: Stridable, Bound.Stride: SignedInteger. You can find out the "numeric" index of an element in such a range by using distance(to: lowerBound), which is a method on Stridable.

    extension ClosedRange where Bound: Strideable, Bound.Stride: SignedInteger {
        func numericIndex(of element: Bound) -> Bound.Stride? {
            if contains(element) {
                return element.distance(to: lowerBound)
            } else {
                return nil
            }
        }
    }
    

    For ClosedRange<Int>, this is basically subtraction. The index of 55 in 30...200 is simply 55 - 30 = 25.