Swift: Swap random elements in array
Video: Swift: Swap random elements in array by Taught by Celeste AI - AI Coding Coach
Swift with Copilot: Swap Random Elements in Array
arr.swapAt(i, j)swaps two indices in place. For two random indices: pickiandjfromarr.indices.randomElement(), thenarr.swapAt(i, j). To shuffle the whole thing, justarr.shuffle().
A small exercise that touches randomness, in-place mutation, and array indices.
The Copilot prompt
// Swap two random elements in the array
var numbers = [10, 20, 30, 40, 50]
Copilot completes:
var numbers = [10, 20, 30, 40, 50]
let i = numbers.indices.randomElement()!
let j = numbers.indices.randomElement()!
numbers.swapAt(i, j)
print(numbers)
// e.g. [10, 40, 30, 20, 50]
Walkthrough
arr.indices
[10, 20, 30].indices
// 0..<3 — Range<Int>
indices returns the valid range of indices for the array. For an Array, that's 0..<count. For ArraySlice and other Collections, it might not start at 0.
randomElement()
[10, 20, 30].indices.randomElement()
// Optional<Int> — e.g. 1
randomElement() works on any Collection and returns nil for empty. Force-unwrap is OK for a non-empty array.
swapAt(_:_:)
var arr = [10, 20, 30, 40]
arr.swapAt(0, 3)
print(arr)
// [40, 20, 30, 10]
swapAt(i, j) swaps the elements at indices i and j in place. Constant time, no allocation. Swaps with itself (i == j) is a no-op.
The array must be var — let doesn't allow mutation.
Edge case: same indices
let i = numbers.indices.randomElement()!
let j = numbers.indices.randomElement()!
// i and j might be the same — swap is a no-op
For a "guaranteed actually-swap":
guard numbers.count >= 2 else { return }
let i = numbers.indices.randomElement()!
var j = numbers.indices.randomElement()!
while j == i {
j = numbers.indices.randomElement()!
}
numbers.swapAt(i, j)
Loop until j != i. For small arrays this is fine; for huge it's still O(1) expected.
Shuffling the whole array
If you want to randomize all positions:
var arr = [1, 2, 3, 4, 5]
arr.shuffle()
print(arr)
// [3, 1, 5, 2, 4] — different each run
shuffle() is in-place. For a copy: arr.shuffled().
Internally uses Fisher-Yates: O(n), unbiased.
Manual Fisher-Yates
extension Array {
mutating func myShuffle() {
for i in stride(from: count - 1, through: 1, by: -1) {
let j = Int.random(in: 0...i)
swapAt(i, j)
}
}
}
Walk from the end backwards; for each i, pick a random j in [0, i], swap. Each element ends up in a uniformly-random position.
You won't write this in real code (shuffle() is built-in), but it's a classic algorithm worth recognizing.
Random sample without replacement
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let three = Array(arr.shuffled().prefix(3))
// 3 unique random elements
shuffled().prefix(N) is the standard idiom for random sampling.
Random pair
let pair = Array(arr.shuffled().prefix(2))
let a = pair[0]
let b = pair[1]
Two distinct elements, randomly chosen.
Modifying via indices loop
var arr = [1, 2, 3, 4, 5]
for i in arr.indices {
if Bool.random() {
let j = arr.indices.randomElement()!
arr.swapAt(i, j)
}
}
A "lazy shuffle" — randomly choose to swap or not at each position. Not a uniform shuffle, but useful for some randomization patterns.
In-place vs return-new
var arr = [1, 2, 3]
arr.shuffle() // mutates, returns Void
let arr2 = [1, 2, 3]
let shuffled = arr2.shuffled() // new array, original unchanged
The <verb> form mutates in place; the <verb>ed form returns a new value. Same convention as sort/sorted, reverse/reversed.
Working with ArraySlice
var arr = [1, 2, 3, 4, 5]
arr[1...3].shuffled() // returns shuffled slice — original unchanged
arr[1...3] is an ArraySlice, not a new array. To mutate the underlying storage:
var slice = arr[1...3]
slice.shuffle() // mutates slice
arr.replaceSubrange(1...3, with: slice)
Or just operate on indices:
for i in 1...3 {
let j = (1...3).randomElement()!
arr.swapAt(i, j)
}
Common stumbles
Mutating let. let arr = [1,2,3]; arr.swapAt(0,1) — error. Use var.
Force-unwrap on empty. [].indices.randomElement()! crashes. Guard with arr.isEmpty first.
Same index swap. swapAt(2, 2) is a no-op, not an error. If you really need a different index, check.
Shuffled vs sorted. shuffled() returns randomly-ordered; sorted() returns ascending. Don't confuse.
Shuffling huge arrays. shuffle() is O(n) — fast, no concern. shuffled() allocates a copy, so 2x memory briefly.
ArraySlice indices not starting at 0. arr[1...3].indices is 1...3, not 0...2. Don't assume zero-based.
What's next
Episode 20: Pass a closure to a function. Higher-order functions, the foundation.
Recap
arr.swapAt(i, j) for in-place swap. arr.indices.randomElement() for a random valid index. For full shuffle: arr.shuffle() (or .shuffled() for a copy). For random sample without replacement: arr.shuffled().prefix(N). Watch out for force-unwrap on empty arrays and same-index swaps. Use var for in-place; let is immutable.
Next episode: passing closures.