Sequences and Collections in Swift 4
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. :)