110

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?

pkamb
  • 26,648
  • 20
  • 124
  • 157
Eduardo
  • 8,112
  • 5
  • 35
  • 70
  • See [my answer](http://stackoverflow.com/a/42467712/1966109) for a similar question that gives up to 8 ways to solve your problem with Swift 3. – Imanou Petit Feb 26 '17 at 15:12

7 Answers7

195

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
}
Jack
  • 16,221
  • 8
  • 44
  • 50
  • FYI Stride has changed since beta 6 – DogCoffee Aug 25 '15 at 06:32
  • 1
    it should be `(1...5).reverse()` (as a function call), please update your answer. (Since it's only two characters, I couldn't edit your post.) – Behdad Sep 26 '15 at 12:06
  • In Swift 3.0-PREVIEW-4 (and possibly earlier), it should be `(1...5).reversed()`. Also, the example for `.stride()` doesn't work, and needs the free function `stride(from:to:by:)` instead. – davidA Aug 12 '16 at 03:26
  • 2
    looks like the `stride` code for swift 3 is slightly incorrect. to include the number 1, you would need to use `through` opposed to `to` like this: `for i in stride(from:5,through:1,by:-1) { print(i) }` – 262Hz Feb 24 '17 at 00:36
  • 4
    It's worth pointing out that `reversed` has a complexity of O(1) because it merely wraps the original collection and doesn't require an iteration to build it. – devios1 Mar 12 '18 at 16:02
24

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()
}
sakiM
  • 4,292
  • 5
  • 23
  • 32
matt
  • 447,615
  • 74
  • 748
  • 977
  • 1
    Wow, the `>>>` operator is really cool! I hope Swift designers pay attention to things like that, because sticking `reverse()` on the end of parenthesized expressions does feel weird. There's no reason they couldn't deal with `5>..0` ranges automatically, because `ForwardIndexType` protocol provides a perfectly reasonable `distanceTo()` operator that can be used to figure out if the stride should be positive or negative. – Sergey Kalinichenko Mar 17 '16 at 16:46
  • 1
    Thanks @dasblinkenlight - This came up for me because in one of my apps I had a _lot_ of C for loops (in Objective-C) that migrated to Swift C-style for loops and now had to be converted because C-style for loops are going away. This was simply terrifying, because these loops represented the core of my app's logic, and rewriting everything risked breakage. Thus I wanted a notation that would make the correspondence with the (commented-out) C-style notation as clear as possible. – matt Mar 17 '16 at 16:54
  • 1
    @matt covers the `>>>` operator in his book [iOS 14 Programming Fundamentals with Swift](https://www.oreilly.com/library/view/ios-14-programming/9781492092087/), Chapter 5, Operators section, p. 302 in my copy (2020-09-23 release). Another fun operator by @matt is `^^` for exponentiation. To quote from the beginning of the Operators section: "Swift operators such as + and > are not magically baked into the language. They are, in fact, functions; they are explicitly declared and implemented just like any other function." (p.299) – Rethunk Feb 01 '21 at 20:43
11

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
}
Mick MacCallum
  • 124,539
  • 40
  • 277
  • 276
  • +1 I'm looking for a reference to `lazy(0....5).reverse()` and I don't see the beta 5 release notes. Do you know of other discussions on this topic? Also, FYI, the numbers inside that `for` loop are backwards in your example. – Rob Oct 20 '14 at 04:05
  • 1
    @Rob Hmm I should have figured that the release notes for a beta would eventually get taken down. When I wrote this, that was the only place I had seen a reference to lazy(), but I'll gladly look around for some more and update/correct this when I get home later. Thanks for pointing this out. – Mick MacCallum Oct 21 '14 at 16:06
  • 1
    @0x7fffffff In your last example, the comment says `// 0, 1, 2, 3, 4, 5` but it actually should be reversed. The comment is misleading. – Pang Jun 29 '15 at 07:37
5

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().

leanne
  • 5,933
  • 38
  • 65
3

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

nCod3d
  • 567
  • 8
  • 11
2

This could be another way of doing this.

(1...5).reversed().forEach { print($0) }
David H
  • 51
  • 2
-2

Reverse() function is used for reverse number.

Var n:Int // Enter number

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