Reverse Range in Swift

Swift

Swift Problem Overview


Is there a way to work with reverse ranges in Swift?

For example:

for i in 5...1 {
  // do something
}

is an infinite loop.

In newer versions of Swift that code compiles, but at runtime gives the error:

> Fatal error: Can't form Range with upperBound < lowerBound

I know I can use 1..5 instead, calculate j = 6 - i and use j as my index. I was just wondering if there was anything more legible?

Swift Solutions


Solution 1 - Swift

Update For latest Swift 3 (still works in Swift 4)

You can use the reversed() method on a range

for i in (1...5).reversed() { print(i) } // 5 4 3 2 1

Or stride(from:through:by:) method

for i in stride(from:5,through:1,by:-1) { print(i) } // 5 4 3 2 1

stide(from:to:by:) is similar but excludes the last value

for i in stride(from:5,to:0,by:-1) { print(i) } // 5 4 3 2 1

Update For latest Swift 2

First of all, protocol extensions change how reverse is used:

for i in (1...5).reverse() { print(i) } // 5 4 3 2 1

Stride has been reworked in Xcode 7 Beta 6. The new usage is:

for i in 0.stride(to: -8, by: -2) { print(i) } // 0 -2 -4 -6
for i in 0.stride(through: -8, by: -2) { print(i) } // 0 -2 -4 -6 -8

It also works for Doubles:

for i in 0.5.stride(to:-0.1, by: -0.1) { print(i) }

Be wary of floating point compares here for the bounds.

Earlier edit for Swift 1.2: As of Xcode 6 Beta 4, by and ReverseRange don't exist anymore :[

If you are just looking to reverse a range, the reverse function is all you need:

for i in reverse(1...5) { println(i) } // prints 5,4,3,2,1

As posted by 0x7fffffff there is a new stride construct which can be used to iterate and increment by arbitrary integers. Apple also stated that floating point support is coming.

Sourced from his answer:

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}

Solution 2 - Swift

There's something troubling about the asymmetry of this:

for i in (1..<5).reverse()

...as opposed to this:

for i in 1..<5 {

It means that every time I want to do a reverse range, I have to remember to put the parentheses, plus I have to write that .reverse() on the end, sticking out like a sore thumb. This is really ugly in comparison to C-style for loops, which are symmetrical counting up and counting down. So I tended to use C-style for loops instead. But in Swift 2.2, C-style for loops are going away! So I've had to scurry around replacing all my decrementing C-style for loops with this ugly .reverse() construct — wondering all the while, why on earth isn't there a reverse-range operator?

But wait! This is Swift — we're allowed to define our own operators!! Here we go:

infix operator >>> {
    associativity none
    precedence 135
}

func >>> <Pos : ForwardIndexType where Pos : Comparable>(end:Pos, start:Pos)
    -> ReverseRandomAccessCollection<(Range<Pos>)> {
        return (start..<end).reverse()
}

So now I'm allowed to say:

for i in 5>>>1 {print(i)} // 4, 3, 2, 1

This covers just the most common case that occurs in my code, but it is far and away the most common case, so it's all I need at present.

I had a kind of internal crisis coming up with the operator. I would have liked to use >.., as being the reverse of ..<, but that's not legal: you can't use a dot after a non-dot, it appears. I considered ..> but decided it was too hard to distinguish from ..<. The nice thing about >>> is that it screams at you: "down to!" (Of course you're free to come up with another operator. But my advice is: for super symmetry, define <<< to do what ..< does, and now you've got <<< and >>> which are symmetrical and easy to type.)


Swift 3 version (Xcode 8 seed 6):
infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound) ->
    ReversedRandomAccessCollection<CountableRange<Bound>> 
    where Bound : Comparable, Bound.Stride : Integer {
        return (minimum..<maximum).reversed()
}
Swift 4 version (Xcode 9 beta 3):
infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<CountableRange<Bound>>
    where Bound : Comparable & Strideable { 
        return (minimum..<maximum).reversed()
}
Swift 4.2 version (Xcode 10 beta 1):
infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<Range<Bound>>
    where Bound : Strideable { 
        return (minimum..<maximum).reversed()
}
Swift 5 version (Xcode 10.2.1):
infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedCollection<Range<Bound>>
    where Bound : Strideable {
        return (minimum..<maximum).reversed()
}

Solution 3 - Swift

It appears that the answers to this question have changed a bit as we've progressed through the betas. As of beta 4, both the by() function and the ReversedRange type have been removed from the language. If you're looking to make a reversed range, your options are now as follows:

1: Create a forward range, and then use the reverse() function to reverse it.

for x in reverse(0 ... 4) {
    println(x) // 4, 3, 2, 1, 0
}

for x in reverse(0 ..< 4) {
    println(x) // 3, 2, 1, 0
}

2: Use the new stride() functions that were added in beta 4, which includes functions to specify the starting and ending indexes, as well as the amount to iterate by.

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}

Note that I've also included the new exclusive range operator in this post as well. .. was replaced with ..<.

Edit: From the Xcode 6 beta 5 release notes, Apple added the following suggestion for handling this:

> ReverseRange has been removed; use lazy(x..

Here's an example.

for i in lazy(0...5).reverse() {
    // 0, 1, 2, 3, 4, 5
}

Solution 4 - Swift

Xcode 7, beta 2 (< Swift 3.0):

for i in (1...5).reverse() {
  // do something
}

Update for Xcode 8.0+ (Swift 3.0+), per Developer Documentation, available as of 8 Dec 2020:

  • To iterate through a collection in reverse, without changing the collection's contents, use reversed -
let word = "Backwards"
for char in word.reversed() {
    print(char, terminator: "")
}
// Prints "sdrawkcaB"

In this case, a wrapper around the collection reads the collection's contents and returns them in reverse order.

  • To reverse the collection's contents, use a type initializer, and provide it the collection reversed. This makes a new collection with the same contents, but reversed:
let reversedWord = String(word.reversed())
print(reversedWord)
// Prints "sdrawkcaB"
  • Additionally, you can use the generic function stride(from:to:by:) to return the collection's contents in reverse (as well as in its normal order):
for countdown in stride(from: 3, to: 0, by: -1) {
    print("\(countdown)...")
}
// 3...
// 2...
// 1...

If the from value is higher than the to value, and the by value is negative, stride reads contents in reverse.

If the from value is lower than the to value, and the by value is positive, stride will read through the contents in their original order.

Note: Collections include arrays, sets, dictionaries, as well as other specialized collections. As long as the collection is iterable, these techniques will work.

2nd Note: as of 8 Dec 2020, Swift versions and their associated Xcode versions can be found at https://swiftly.dev/swift-versions. According to this reference, Xcode 8.0+ is associated with Swift 3.0, which is when reverse() became reversed().

Solution 5 - Swift

Swift 3, 4+: you can do it like this:

for i in sequence(first: 10, next: {$0 - 1}) {

    guard i >= 0 else {
        break
    }
    print(i)
}

result: 10, 9, 8 ... 0

You can customise it any way you like. For more info read func sequence<T> reference

Solution 6 - Swift

This could be another way of doing this.

(1...5).reversed().forEach { print($0) }

Solution 7 - Swift

Reverse() function is used for reverse number.

Var n:Int // Enter number

For i in 1...n.reverse() { Print(i) }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEduardoView Question on Stackoverflow
Solution 1 - SwiftJackView Answer on Stackoverflow
Solution 2 - SwiftmattView Answer on Stackoverflow
Solution 3 - SwiftMick MacCallumView Answer on Stackoverflow
Solution 4 - SwiftleanneView Answer on Stackoverflow
Solution 5 - SwiftnCod3dView Answer on Stackoverflow
Solution 6 - SwiftDavid HView Answer on Stackoverflow
Solution 7 - SwiftlaxrajpurohitView Answer on Stackoverflow