Higher Order Functions in Swift
Tags: swift, tutorial
Published: 2020-01-14 18:55:26

### Parameterizing behaviour with functions in other HOCs

These transforming HOCs like `map`, `filter` are so useful for the reason of them having the transformation function acting inside, specified by the programmer.

This pattern of **parameterized** behaviour is found throughout the standard library. `Array`, and other collections, have several useful HOCs 

- `map` and `flatMap` transform elements
- `filter` include only certain elements
- `allSatisfy` test all elements for condition
- `reduce` fold the elements into an aggregate value
- `sort(by:)`,`sorted(by:)`, `lexicographicallyPrecedes(_:by:)`, and `partition(by:)` all reorder elements
```text
recipes.sort {$0.prepTime < $1.prepTime }

recipes.sort { $0.label.uppercased() < $1.label.uppercased() }

```
- `forEach` visit each element
- `firstIndex(where:)`, `lastIndex(where:)`, `first(where:)`, `last(where:)`, and `contains(where:)` answer "Does element exist ?"
```text
bulletproofRice.ingredients.contains { $0.ingredient == "Raw Honey" }
```
- `min(by:)`, `max(by:)` find the minimum or maximum of all elements
- `elementsEqual(_:by:)` and `starts(with:by:)` compare elements of collections
- `split(whereSeparator:)` break up the elements into multiple arrays
- `prefix(while:)` take elements from the start as long as the condition holds true
- `drop(while:)` drop elements until the condition holds true, return the rest. The inverse function of `prefix`
- `removeAll(where:)` remove the elements matching the condition
- `accumulate` combine elements into an array. Alike `reduce`, but returns multiple times after each action.
- `count(where:)` count the number of elements that match
- `indices(where:)` return a list of indices matching the condition. Alike `firstIndex` but doesn't halt on the first.
- `zip(sequence1:sequence2:)` create a sequence of pairs built out of two underlying sequences.

##### Consider my Wild-Caught oneliner
```text
let numbers: [Int] = [0, 1, 1, 2, 3, 4, 4]
var result: [[Int]] = numbers.isEmpty ? [] : [[numbers[0]]]
//Wild-Caught one-liner
for (previous, current) in zip(numbers, numbers.dropFirst()) {
    previous == current ? result[result.endIndex - 1].append(current) : result.append([current])
}
result // [[0], [1, 1], [2], [3], [4, 4]]
```
What it does, or, I think it does, is splits an array into groups of adjacent equal elements.
If to put it succinctly into an extension
```
extension Array {
func split(where condition: (Element, Element) -> Bool) -> [[Element]] {
    var result: [[Element]] = numbers.isEmpty ? [] : [[self[0]]]
        for (previous, current) in zip(self, self.dropFirst()) {
            if condition(previous, current) {
                result.append([current])
            }
            else {
                result[result.endIndex - 1].append(current)
            }
        }
        return result
    }
}

result = numbers.split { $0 != $1 }
result // [[0], [1, 1], [2], [3], [4, 4]]
result = numbers.split(where: !=)
result // same result
```

The overall idea is to remove as much of boilerplate as possible. Several of these functions have default implementation: `sort`, for example, sorts in ascension; `contains` takes a value to check for. 

#### That's the satori of modern programming languages.