Swift — Operate on collection using closure

Kent Winder
Nextzy
Published in
3 min readOct 23, 2017

--

Sometimes when you find yourself looping over a collection, your first instinct is to use the familiar for-loop. In Swift colletion types such as Array or Dictionary or Set, there are a few methods that help you do so with cleaner code. These methods use closure which accepts another function that will determine how to handle the collection.

.sort() / .sorted()

When using .sort() the items in the array itself are sorted, while .sorted() will return a copy of the array with the values sorted.

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let decendingNumbers = numbers.sorted{ (n1: Int, n2: Int) -> Bool in
return n1 > n2
}
print(decendingNumbers)
// [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

The above code is the general form of closure expression syntax, but this syntax can be reduced, more details can be viewed at The Swift Programming Language

  • Inferring Type From Context (omit type)
decendingNumbers = numbers.sorted(by: { n1, n2 in return n1 > n2 })
  • Implicit Returns from Single-Expression Closures
decendingNumbers = numbers.sorted(by: { n1, n2 in n1 > n2 })
  • Shorthand Argument Names
decendingNumbers = numbers.sorted(by: { $0 > $1 })
  • Or even shorter with Operator Methods
decendingNumbers = numbers.sorted(by: >)

.filter()

Use filter to loop over an array and return only those items that match the condition.

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let evenNumbers = numbers.filter { (n) -> Bool in
return n % 2 == 0
}
// short syntax
// let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)
// [6, 0, 4, 2, 8]

.map() / .flatMap()

Mapping is similar to sorting but instead of sorting, it apply the same operation from the closure to each element in the collection

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let squaredNumbers = numbers.map { (n) -> Int in
return n * n
}
// short syntax
// let squaredNumbers = numbers.map { $0 * $0 }
print(squaredNumbers)
// [25, 36, 0, 9, 1, 81, 16, 4, 64, 49]

You can have the same result using flatMap, but usually flatMap is used to flatten a collection of collections.

let collections = [[5, 6, 0], [3, 1, 9, 4], [2, 8, 7]]
let numbers = collections.flatMap { (collection) -> [Int] in
return collection
}
// short syntax
// let numbers = collections.flatMap { $0 }
print(numbers)
// [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]

…or even create an array from the filtered items of the sub arrays

let oddNumbers = collections.flatMap { (collection) -> [Int] in
collection.filter({ (number) -> Bool in
return number % 2 != 0
})
}
// short syntax
// let oddNumbers = collections.flatMap { collection in
// collection.filter { $0 % 2 != 0 }
// }
print(oddNumbers)
// [5, 3, 1, 9, 7]

.reduce()

Use reduce to combine all items in an array to create an object of any type. For example, getting sum of all numbers

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let total = numbers.reduce(0) { (result, n) -> Int in
return result + n
}
// short syntax
// let total = numbers.reduce(0) { $0 + $1 }
print(total)
// 45

…or combining all numbers into a string

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let allNumbers = numbers.reduce("") { (result, n) -> String in
return result + String(n)
}
// short syntax
// let allNumbers = numbers.reduce("") { $0 + String($1) }
print(allNumbers)
// "5603194287"

Chaining

You can chain methods. For example to sum only even numbers, we can first filter and then reduce:

let numbers = [5, 6, 0, 3, 1, 9, 4, 2, 8, 7]
let totalOfEvenNumbers = numbers.filter { $0 % 2 == 0 }.reduce(0, +)
print(totalOfEvenNumbers)
// 20

I hope this post will be helpful next time you find yourself in these situations

--

--

Kent Winder
Nextzy

Just a regular guy who loves coding, reading, and getting tattoos