Swift: Swift Ranges and Sequences

A range is like a segment on a number line — consecutive integers from A to B. This lesson covers how to create, use, and customize all range and sequence types in Swift.

1. What You'll Learn


2. An Event Planner's Real Story

(1) Pain: Manually generating 200 seat numbers — Excel froze

Charlie is organizing a 200-person tech conference. He needs to generate seat numbers: rows A through T (20 rows), 10 seats per row, assign attendees to seats, and produce a seating chart. He started by dragging the fill handle in Excel, but every time he added a VIP seat he had to readjust the entire sheet:

SWIFT
let seatA1 = "A1"
let seatA2 = "A2"
// ... 200 variables total

By seat #150, Excel crashed. Charlie realized: he needed programmatic seat number generation.

(2) The Range and Stride Solution

SWIFT
let rows = "ABCDEFGHIJKLMNOPQRST"
let seatsPerRow = 10
for row in rows {
    for seat in 1...seatsPerRow {
        print("\(row)\(seat)", terminator: " ")
    }
    print()
}

(3) Result: 5 hours → 3 seconds

Metric Dragging in Excel Swift Range Generation
200 seats 5 hours 3 seconds
Change seating layout Re-drag 10 minutes Change one number
Spaced seats Manual lookup stride generates directly
Error rate High 0

3. Range and ClosedRange

Range (half-open ..<) includes the start value but excludes the end. ClosedRange (closed ...) includes both endpoints.

100%
graph LR
    A["1...5"] --> B[1]
    A --> C[2]
    A --> D[3]
    A --> E[4]
    A --> F[5]
    G["1..<5"] --> H[1]
    G --> I[2]
    G --> J[3]
    G --> K[4]
Range Type Syntax Includes Example Result
ClosedRange a...b a, a+1, ..., b 1...3 -> 1,2,3
Range a..<b a, a+1, ..., b-1 1..<3 -> 1,2
PartialFrom a... a to infinity Array slice array[2...]
PartialThrough ...b ... to b Array slice array[...2]

(1) Creating and Traversing Ranges

SWIFT
// Closed range: includes 1 through 5
let closed = 1...5
for i in closed {
    print(i, terminator: " ")
}
print()
// Half-open range: includes 1 through 4
let halfOpen = 1..<5
for i in halfOpen {
    print(i, terminator: " ")
}
print()
// Reverse traversal
for i in (1...5).reversed() {
    print(i, terminator: " ")
}
print()

(2) Ranges in Array Slicing

SWIFT
let numbers = [10, 20, 30, 40, 50, 60, 70]
let firstThree = numbers[0..<3]
print(firstThree)
let fromSecond = numbers[2...]
print(fromSecond)
let firstFour = numbers[...3]
print(firstFour)

▶ Example: Exam Grading with Range Matching

SWIFT
// ============================================
// Using ranges in switch for grade classification
// ============================================
let scores = [95, 82, 67, 54, 43, 78, 91]
for score in scores {
    let grade: String
    switch score {
    case 90...100:
        grade = "A"
    case 80..<90:
        grade = "B"
    case 70..<80:
        grade = "C"
    case 60..<70:
        grade = "D"
    case 0..<60:
        grade = "F"
    default:
        grade = "Invalid"
    }
    print("Score \(score): Grade \(grade)")
}

Output:

TEXT 📖 Display only
Score 95: Grade A
Score 82: Grade B
Score 67: Grade D
Score 54: Grade F
Score 43: Grade F
Score 78: Grade C
Score 91: Grade A

4. Stride and Custom Sequences

Stride lets you traverse with a custom step value rather than incrementing by 1 every time. Custom sequences let you define your own iteration logic.

100%
graph TB
    A[Sequence] --> B["stride(from: 0, to: 10, by: 2)"]
    A --> C["stride(from: 10, through: 0, by: -2)"]
    B --> D["0, 2, 4, 6, 8"]
    C --> E["10, 8, 6, 4, 2, 0"]
Function Range Type Endpoint Inclusive Example
stride(from:to:by:) Half-open No stride(from:0, to:10, by:3) -> 0,3,6,9
stride(from:through:by:) Closed Yes stride(from:0, through:10, by:3) -> 0,3,6,9

(1) Basic Stride Usage

SWIFT
// 0 to 9, step 2
for i in stride(from: 0, to: 10, by: 2) {
    print(i, terminator: " ")
}
print()
// 10 to 0, step -3
for i in stride(from: 10, through: 0, by: -3) {
    print(i, terminator: " ")
}
print()
// Floating-point stepping
for temp in stride(from: 0.0, to: 1.0, by: 0.25) {
    print("\(Int(temp * 100))%", terminator: " ")
}
print()

(2) Custom Sequences

SWIFT
struct FibonacciSequence: Sequence {
    let count: Int
    func makeIterator() -> FibonacciIterator {
        return FibonacciIterator(count: count)
    }
}
struct FibonacciIterator: IteratorProtocol {
    let count: Int
    var current = 0
    var nextValue = 1
    var index = 0
    mutating func next() -> Int? {
        guard index < count else { return nil }
        defer { index += 1 }
        let result = current
        current = nextValue
        nextValue = result + current
        return result
    }
}
let fib = FibonacciSequence(count: 10)
for num in fib {
    print(num, terminator: " ")
}
print()

▶ Example: Temperature Conversion Table Generator

SWIFT
// ============================================
// Generate temperature conversion table with stride
// ============================================
print("Celsius\tFahrenheit")
print("-----------------")
for celsius in stride(from: 0.0, through: 100.0, by: 10.0) {
    let fahrenheit = celsius * 9 / 5 + 32
    print("\(Int(celsius))\t\(Int(fahrenheit))")
}
print()
print("Fahrenheit\tCelsius")
print("-------------------")
for f in stride(from: 212.0, through: 32.0, by: -20.0) {
    let c = (f - 32) * 5 / 9
    print("\(Int(f))\t\t\(Int(c))")
}

Output:

TEXT 📖 Display only
Celsius	Fahrenheit
-----------------
0		32
10		50
20		68
... (intermediate rows omitted)
100		212

Fahrenheit	Celsius
-------------------
212		100
192		88
... (intermediate rows omitted)
32		0

5. zip and Sequence Combination

zip pairs two sequences by index, producing a sequence of tuples. It stops automatically when either sequence ends.

Characteristic Description
Input Two sequences
Output Sequence of tuples (Element1, Element2)
Stop condition Whichever sequence ends first
Common uses Parallel traversal, data pairing, index generation

(1) Basic zip Usage

SWIFT
let names = ["Alice", "Bob", "Charlie", "Diana"]
let scores = [88, 92, 75, 95]
for (name, score) in zip(names, scores) {
    print("\(name): \(score)")
}
let short = ["A", "B", "C"]
let long = [1, 2, 3, 4, 5]
for pair in zip(short, long) {
    print(pair)
}

(2) zip + Range for Index Generation

SWIFT
let fruits = ["Apple", "Banana", "Orange", "Grape"]
for (index, fruit) in zip(1..., fruits) {
    print("\(index). \(fruit)")
}

▶ Example: Seat Assignment System

SWIFT
// ============================================
// Assign attendees to seats using zip and ranges
// ============================================
let attendees = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
let rows = ["A", "B", "C", "D"]
let seatsPerRow = 4
var allSeats: [String] = []
for row in rows {
    for seat in 1...seatsPerRow {
        allSeats.append("\(row)\(seat)")
    }
}
print("=== Seat Assignments ===")
for (attendee, seat) in zip(attendees, allSeats) {
    print("\(seat): \(attendee)")
}
let vipCount = 2
let vipSeats = allSeats.prefix(vipCount)
let vipAttendees = attendees.prefix(vipCount)
print("\n=== VIP Section ===")
for (person, seat) in zip(vipAttendees, vipSeats) {
    print("\(seat): \(person)")
}

Output:

TEXT 📖 Display only
=== Seat Assignments ===
A1: Alice
A2: Bob
A3: Charlie
A4: Diana
B1: Eve
B2: Frank

=== VIP Section ===
A1: Alice
A2: Bob

6. Full Example: Exam Seating Chart and Grade Analysis

SWIFT
// ============================================
// Exam seating chart and grade analysis
// Combining Range / Stride / zip / Sequence
// ============================================
import Foundation
let examRows = 5
let cols = 4
print("=== Exam Seating Chart ===")
for row in 0..<examRows {
    let rowLabel = Character(UnicodeScalar(65 + row)!)
    for col in 1...cols {
        print("\(rowLabel)\(col)", terminator: "\t")
    }
    print()
}
let students = ["Alice", "Bob", "Charlie", "Diana", "Eve",
                "Frank", "Grace", "Henry", "Ivy", "Jack",
                "Kevin", "Leo", "Mia", "Noah", "Olivia",
                "Paul", "Quinn", "Rose", "Sam", "Tina"]
let examSeats = examRows * cols
let assignedIndices = stride(from: 0, to: min(students.count, examSeats), by: 1)
print("\n=== Seat Assignments ===")
for (index, studentIndex) in zip(assignedIndices, 0..<students.count) {
    let row = index / cols
    let col = index % cols + 1
    let rowLabel = Character(UnicodeScalar(65 + row)!)
    print("\(rowLabel)\(col): \(students[studentIndex])")
}
print("\n=== Score Report ===")
var scores: [String: Int] = [:]
for student in students.prefix(examSeats) {
    scores[student] = Int.random(in: 40...100)
}
let gradeRanges: [ClosedRange<Int>: String] = [
    90...100: "A",
    80...89: "B",
    70...79: "C",
    60...69: "D",
    0...59: "F"
]
var gradeCount: [String: Int] = ["A": 0, "B": 0, "C": 0, "D": 0, "F": 0]
for (student, score) in scores.sorted(by: { $0.key < $1.key }) {
    var grade = "F"
    for (range, g) in gradeRanges {
        if range.contains(score) {
            grade = g
            break
        }
    }
    gradeCount[grade]! += 1
    print("\(student): \(score) -- \(grade)")
}
print("\n=== Grade Distribution ===")
for grade in ["A", "B", "C", "D", "F"] {
    let count = gradeCount[grade]!
    let bar = String(repeating: "#", count: count)
    print("\(grade): \(bar) (\(count))")
}

Output:

TEXT 📖 Display only
=== Exam Seating Chart ===
A1	A2	A3	A4
B1	B2	B3	B4
C1	C2	C3	C4
D1	D2	D3	D4
E1	E2	E3	E4

=== Seat Assignments ===
A1: Alice
A2: Bob
... (intermediate rows omitted)
E4: Tina

=== Score Report ===
Alice: 82 -- B
Bob: 67 -- D
... (intermediate rows omitted)

=== Grade Distribution ===
A: ### (3)
B: ##### (5)
C: #### (4)
D: ### (3)
F: # (1)

❓ FAQ

Q What's the difference between ... and ..<?
A a...b includes b (closed range). a..<b excludes b (half-open range). For example, 1...3 traverses 1,2,3; 1..<3 traverses 1,2.
Q Can ranges be used with non-integer types?
A Yes. Any type that conforms to Comparable works, such as Double or Character: 1.0...5.0 or "a"..."z". Note that Double ranges cannot be used with for-in (step ambiguity).
Q Can the stride by parameter be negative?
A Yes. A negative value creates a descending traversal. Remember to swap the from and to/through order — larger first, smaller second.
Q How long is the sequence returned by zip?
A The length of the shorter input sequence. zip stops when either sequence ends; it does not pad with nil.
Q How do I check if a value falls within a range?
A Use the contains() method: (1...10).contains(5) returns true. Or match in a switch: case 1...10:.
Q Does array slicing with array[2...] copy the array?
A No. Array slicing returns an ArraySlice that shares the original array's memory — no copy is made. The slice has its own index starting offset.

📖 Summary


📝 Exercises

  1. Beginner: Use 1...10 to print the 3rd row of a multiplication table (31 through 310), formatted as "3 x 1 = 3".
  2. Intermediate: Use stride to generate even numbers from 100 down to 0 with a step of -2, and calculate their sum.
  3. Challenge: Create a custom sequence WeekdaySequence that starts from a given weekday (e.g. "Wed") and loops indefinitely through ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]. Use zip to pair with the first 14 integers and print "Day 1: Wed", "Day 2: Thu"...
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏