Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advance all elements in a Range in Swift?

Tags:

range

swift

If I have a Range, say

let bilbo = ( 1 ... 5 )

And I wanted to advance all elements of it by a number, say 3, is there a way other than

let offset = 3
let baggins = ( bilbo.first! + offset ... bilbo.last! + offset )

Something like

bilbo.advance_by( 3 )

Which I discovered only works for one element in the Range?

I have searched the web and SO and can't find an answer. I'm guessing that there is a Swift-ish way to do this that probably appears in another SO post somewhere that I just don't comprehend how it connects yet. Any help would be appreciated.

like image 351
Dribbler Avatar asked Dec 03 '25 22:12

Dribbler


2 Answers

let advanceRangeBy : (Range<Int>, Int) -> Range<Int> = { $0.0.first!.advancedBy($0.1) ... $0.0.last!.advancedBy($0.1) }

let bilbo = 1...5
let bagger = advanceRangeBy(bilbo, 3) // 4..<9

You can also make it a generic extension to Range that will work for many (although not all) types of Range:

let bilbo = 1...5

extension Range where Element : BidirectionalIndexType {
    func advanceRangeBy(advance: Element.Distance) -> Range<Element> {
        return first!.advancedBy(advance) ... last!.advancedBy(advance)
    }
}

let baggins = bilbo.advanceRangeBy(3)

For the sake of completeness, I thought I'd add that you can also perform this range advancement/de-advancement operation using a custom binary infix operator

infix operator <> {
    associativity left
    precedence 140     /* use same precedence as '+', '-' arithmetic */
}
func <> (lhs: Range<Int>, rhs: Int) -> Range<Int>{
    var out : Range<Int> = lhs
    out.endIndex = lhs.endIndex + rhs
    out.startIndex = lhs.startIndex + rhs
    return out
}

let bilbo = 1...5
let bagger = bilbo <> 3    // 4..<9
let frodo = bagger <> (-2) // 2..<7
like image 148
dfrib Avatar answered Dec 05 '25 11:12

dfrib


How about a simple extension:

extension Range {

       public func advancedBy(n: Element.Distance) -> Range<Element> {
    
       let startIndex = self.startIndex.advancedBy(n)
       let endIndex = self.endIndex.advancedBy(n)
    
       return Range(start: startIndex, end: endIndex)
    }
}

Update for swift 5:

public extension CountableRange {
    func advanced(by n: Bound.Stride) -> Self {
        
        let lowerBound = self.lowerBound.advanced(by: n)
        let upperBound = self.upperBound.advanced(by: n)
        
        return .init(uncheckedBounds: (lowerBound, upperBound))
    }

    static func + (lhs: Self, rhs: Bound.Stride) -> Self {
        lhs.advanced(by: rhs)
    }
}

Note that CountableRange is just a typealias for Range with conditions:

typealias CountableRange<Bound> = Range<Bound> where Bound : Strideable, Bound.Stride : SignedInteger 
like image 22
KoCMoHaBTa Avatar answered Dec 05 '25 12:12

KoCMoHaBTa



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!