Sequences and Collections in Swift 4

Swift — Map, compactMap, Filter and Reduce

Sequences and Collections in Swift 4

Swift — Map, compactMap, Filter and Reduce

When we’re working with Swift, we need an ordered list of elements. 9 times out of 10 we need to reach for an array. But array and all the other Collection protocols in or Collection objects in Swift are built off of a well thought out hierarchy of protocols, associated types and various other components that add to functionality that we use and we take for granted day to day.

In this article, I am going to cover three most asked questions related to collection/sequences.

Q: What is map/filter/reduce API, what its return type and what’s the purpose of using it?

Lets start!!!

Sequences and collections implement methods such as map(_:), filter(_:), and reduce(_:_:) to consume and transform their contents. You can compose these methods together to efficiently implement complex algorithms.

The map(_:) method returns a new array by applying a transform to each element in a collection. For example, the following code multiplies the quantity of the ingredient by the cost of the ingredient to find the total cost for each ingredient.

let totalPrices = shoppingList.map { ingredient in
     return ingredient.quantity * ingredient.price
}

A collection’s filter(_:) method returns an array containing only the elements that pass the provided test. Let’s create a shopping list by filtering out ingredients that have already been purchased.

let shoppingList = sampleIngredients.filter { ingredient in
return !ingredient.purchased
}

You can use the reduce(_:_:) method to combine elements of an array into a single value. The reduce(_:_:) method takes an initial value to start with and then a closure or function to combine each element in the array with the previous value. The following code takes the total price list and adds them together to compute a final remaining cost:

let sum = totalPrices.reduce(0) { currentPrice, priceToAdd in
     return currentPrice + priceToAdd
}

compactMap(_:)

Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.

Use this method to receive an array of nonoptional values when your transformation produces an optional value.

In this example, note the difference in the result of using map and compactMap with a transformation that returns an optional Int value.

let possibleNumbers = ["1", "2", "three", "///4///", "5"] 
let mapped: [Int?] = possibleNumbers.map { str in 
Int(str)
}
// [1, 2, nil, nil, 5]
let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) } 
// [1, 2, 5]

Sequence

We’re going to start from the bottom level: Sequence (very straight forward). Sequence is a list of elements. It has two important caveats: 1, it can be either finite or infinite, and 2, you can only ever iterate through it one time. Sometimes you will be able to iterate it more than once, but your not guaranteed to be able to iterate it more than once.

protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}

The Sequence protocol has two components. One is an associated type, which is the iterator. That associated type has to conform to the Iterator protocol, and it has a function which constructs an Iterator for us and that function has to be the same type as the Iterator that we declared.

Collection

After Sequence, we go up one level to Collection. Every Collection inherits from Sequence and Collection fixes those two problems that we have with Sequence. Every Collection will always be finite. That means that you will always know how many elements are in there. It can never be infinite, and you can iterate that Collection as many times as you want. With Sequence you can only iterate once, but with Collection you can iterate it over and over and over again which is great.

Let’s take a look at the Collection protocol.

protocol Collection {
associatedtype Index: Comparable
var startIndex: Index
var endIndex: Index
subscript(position: Index) -> Iterator.Element { get }
func index(after index: Index) -> Index
}

Accessing Slices of a Collection

You can access a slice of a collection through its ranged subscript or by calling methods like prefix(while:) or suffix(_:). A slice of a collection can contain zero or more of the original collection’s elements and shares the original collection’s semantics.

The following example creates a firstWord constant by using the prefix(while:) method to get a slice of the text string.

let text = "Buffalo buffalo buffalo buffalo."
let firstWord = text.prefix(while: { $0 != " " }) 
print(firstWord)
// Prints "Buffalo"

You can retrieve the same slice using the string’s ranged subscript, which takes a range expression.

if let firstSpace = text.firstIndex(of: " ") {     
print(text[..<firstSpace]
// Prints "Buffalo"
}

The retrieved slice of text is equivalent in each of these cases.

This article covers only few important API’s which are currently asked by interviewers.

Hope you like this article , Please ❤️ to recommend this post to others 😊. Let me know your feedback. :)