Since I learned about functions and closures in detail, I’ve wanted to understand their advantages and use in programming, and my understanding is that higher-order functions are used based on collection types.
A higher-order function, as I understand it, takes another function or closure as an argument and returns a value.
First, let me explain. Consider the following code to give you a better understanding of higher-order functions:
Func addation(num1: Double, num2: Double) -> Double {return num1 + num2
}
func multiply(num1: Double, num2: Double) -> Double {
return num1 * num2
}
func doMathOperation(operation: (_ x: Double, _ y: Double) -> Double, num1: Double, num2: Double) -> Double {
return operation(num1, num2)
}
// print 12
doMathOperation(operation: addation(num1:num2:), num1: 10, num2: 2)
//print 20
doMathOperation(operation: multiply(num1:num2:), num1: 10, num2: 2)
Copy the code
Multiply (num1:num2: 🙂 and multiply(num1:num2: 🙂 are both functions of (Double,Double)->Double. Addation (num1:num2:) takes two Double values and returns their sum; Multiply (num1:num2:) takes the value of two doubles and returns their product. DoMathOperation (operation:num1:num2:) is a higher-order function that takes two Double arguments and a function of type (Double,Double)->Double. By looking at this call, you should understand how higher-order functions work.
In Swift, functions and closures are first-class citizens. They can be stored and passed in variables.
// The function returns the value of another function funcdoArithmeticOperation(isMultiply: Bool) -> (Double, Double) -> Double {
func addation(num1: Double, num2: Double) -> Double {
return num1 + num2
}
func multiply(num1: Double, num2: Double) -> Double {
return num1 * num2
}
return isMultiply ? multiply : addation
}
let operationToPerform1 = doArithmeticOperation(isMultiply: true)
let operationToPerform2 = doArithmeticOperation(isMultiply: false)
operationToPerform1(10, 2) //20
operationToPerform2(10, 2) //12
Copy the code
In the code above, doArithmeticOperation(isMultiply:) is a higher-order function that returns a value of type (Double,Double)->Double. It determines the return value based on a Boolean value. OperationToPerform1 performs multiplication and operationToPerform2 performs addition. With the above function definition and call, you understand everything.
Of course, you can achieve the same thing in many different ways. You can use closures instead of functions, and you can use enumerations to determine what functions do. Here, I just use the above code to explain what a higher-order function is.
Here are some higher-order functions that come with the Swift library. If I understand you correctly, the following functions take closure type arguments. You can use them in an Array Set Dictionary. If you don’t know much about closures, you can learn from this article.
Map
What the map function does is loop through a collection and do the same thing to each element within the loop. It returns an array of mapped elements.
The Map on the Array:
Suppose we have an array of integers:
letArrayOfInt =,3,4,5,4,7,2 [2]Copy the code
How do we multiply every number by 10? We typically use for-in to iterate over each element and then perform the related operations.
var newArr: [Int] = []
for value in arrayOfInt { newArr.append(value*10) }
print(newArr) // prints [20, 30, 40, 50, 40, 70, 20]
Copy the code
The code above looks a bit redundant. It contains boilerplate code for creating a new array, which we can avoid by using map. We can see from Swift’s autocomplete function that the map function takes an Int and returns a generic closure.
let newArrUsingMap = arrayOfInt.map { $0* 10}Copy the code
For an integer array, this is a minimalist version of Map. We can use the $operator in the closure to refer to each element iterated through.
The following code does the same thing; it represents a process from complexity to simplicity. The following code should give you a good idea of closures.
How map works: The map function takes a closure as an argument and calls that closure when iterating over a collection. This closure maps the elements in the collection and returns the result. The map function returns the result in an array.
The Map on the Dictionary:
Let’s say we have a dictionary of titles as keys and book prices as values.
letBookAmount = [" harrypotter ": 100.0," junglebook ": 100.0]Copy the code
If you try to map the dictionary, Swift’s autocomplete will look like this:
let bookAmount = ["harrypotter": 100.0."junglebook": 100.0]
let returnFormatMap = bookAmount.map { (key, value) in
return key.capitalized
}
print(returnFormatMap)
//["Junglebook"."Harrypotter"]
Copy the code
We iterate over a dictionary with the closure containing a key of type String and a value of type Double. Returns an array of strings with an uppercase initial, which can also be prices or tuples, depending on your needs.
Note: the map function always returns a generic array. You can return an array of any type.
Map on set:
letLengthInMeters: Set = [4.0, 6.2, 8.9]let lengthInFeet = lengthInMeters.map { $0* 3.2808}print(lengthInFeet) / / [20.340960000000003, 13.1232, 29.199120000000004]Copy the code
In the code above, we have a set with a value of type Double, and our closure returns a value of type Double. LengthInMeters are a set, and lengthInFeet is an array.
What do you do if you want to get index from a map?
The answer is simple, you must call Enumerate before Map.
Here is the sample code:
let numbers = [1, 2, 4, 5]
let indexAndNum = numbers.enumerated().map { (index,element) in
return "\(index):\(element)"
}
print(indexAndNum) // [" 0:1 ", "1:2", "2:4", "3:5"]Copy the code
Filter
The filter iterates through the collection, returning a collection of elements that match the criteria.
Filter on array
If we were to filter even numbers from an array of integers, you might write the following code:
let arrayOfIntegers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var newArray = [Int]()
for integer in arrayOfIntegers {
if integer % 2 == 0 {
newArray.append(integer)}}print(newArray) //[2, 4, 6, 8]
Copy the code
Like map, this is a simple function to filter the elements of a collection.
If we use filter on an integer array, Swift’s auto-completion is shown as follows:
As you can see, the filter function calls a closure that takes an Int, returns a Bool, and is named isIncluded. IsIncluded returns a Boolean value for each iteration, and then creates a new array containing the filter results based on the Boolean value.
We can change the above code with filter to:
var newArray = arrayOfIntegers.filter { (value) -> Bool in
return value % 2 == 0
}
Copy the code
The filter closure can also be simplified, like map:
Filter on dictionary
Let’s say I have a dictionary of titles as keys and book prices as values.
let bookAmount = ["harrypotter": 100.0."junglebook": 1000.0]Copy the code
If you wanted to call filter on the dictionary, Swift’s autocompletion would look like this:
The filter function calls a closure named isIncluded that takes a key-value pair as an argument and returns a Boolean value. Finally, based on the returned Boolean value, the filter function decides whether to add the key-value pair to the array.
Calling the Filter function on a dictionary returns an array of tuples. However, the translator found in the playground that the return value was actually an array of dictionary types.
let bookAmount = ["harrypotter": 100.0."junglebook": 1000.0]
let results = bookAmount.filter { (key, value) -> Bool in
return value > 100
}
print(results) //["junglebook": 1000.0]
Copy the code
You can also simplify the above code:
//$0For the keyThe $1For the valuelet results = bookAmount.filter { The $1> 100}Copy the code
Filter on set
let lengthInMeters: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let lengthInFeet = lengthInMeters.filter { $0> 5}print(lengthInFeet) //[9, 8, 7, 6]
Copy the code
On each pass, the Filter closure takes a Double and returns a Boolean. Filter the elements contained in the array based on the returned Boolean value.
Reduce
Reduce: combines all the values in the collection and returns a new value.
Apple’s official documentation is as follows:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
Copy the code
The reduce function takes two arguments:
- The first is the initial value, which is used to store the initial value and the return value in each iteration.
- The other argument is a closure that takes two arguments: the initial value or the result of the current operation, and the next item in the collection.
Reduce on arrays
Let’s take an example to understand what Reduce does:
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0) { (x, y) in
return x + y
}
print(numberSum) // 10
Copy the code
The reduce function iterates four times.
1. The initial value is 0, x is 0, y is 1 -> return x + y. So the initial value or the result becomes 1. 2. The initial value or result is 1, x is 1, y is 2 -> return x + y. So the initial value or the result becomes 3. 3. The initial value or result is 3, x is 3, y is 3 -> return x + y. So the initial value or the result becomes 6. 4. The initial value or result is 6, x is 6, y is 4 -> return x + y. So the initial value or the result becomes 10.Copy the code
The reduce function can be simplified as:
let reducedNumberSum = numbers.reduce(0) { $0 + The $1 }
print(reducedNumberSum) // prints 10
Copy the code
In this case, the closure is of type (Int,Int)->Int. So, we can pass any function or closure of type (Int,Int)->Int. For example, we can replace the operators with -, *, /, etc.
let reducedNumberSum = numbers.reduce(0,+) // returns 10
Copy the code
We can add * or other operators to closures.
let reducedNumberSum = numbers.reduce(0) { $0 * The $1 }
// reducedNumberSum is 0...
Copy the code
The above code could also be written like this:
let reducedNumberSum = numbers.reduce(0,*)
Copy the code
Reduce can also combine strings with the + operator.
let codes = ["abc"."def"."ghi"]
let text = codes.reduce("") { $0 + The $1} //the result is "abcdefghi"
or
let text = codes.reduce("",+) //the result is "abcdefghi"
Copy the code
Reduce on dictionary
Let’s reduce bookAmount.
let bookAmount = ["harrypotter": 100.0."junglebook": 1000.0]let reduce1 = bookAmount.reduce(10) { (result, tuple) in
return result + tuple.value
}
print(reduce1) / / 1110.0let reduce2 = bookAmount.reduce("book are ") { (result, tuple) in
return result + tuple.key + ""
}
print(reduce2) //book are junglebook harrypotter
Copy the code
For dictionaries, the reduce closure takes two arguments.
1. An initial value or result that should be reduced. 2Copy the code
Reduce2 can be simplified as:
let reducedBookNamesOnDict = bookAmount.reduce("Books are ") { $0 + The $1.key + "" } //or $0 + The $1. 0 +""
Copy the code
Reduce on set
The use of reduce in Set is the same as that in array.
letLengthInMeters: Set = [4.0, 6.2, 8.9]let reducedSet = lengthInMeters.reduce(0) { $0 + The $1 }
print(reducedSet) / / 19.1Copy the code
The return value in the closure is of type Double.
Flatmap
Flatmap is used to smooth collections in collections. Before we pave the collection, we perform a map operation on each element. Apple Docs Returns an array containing the concatenated results of calling the given transformation with each element of this sequence.)
Map + (Flat the Collection)Copy the code
Figure 1 illustrates the code for flatMap
Figure 2
In Figure 2, the flatMap iterates through all collections in a collection to capitalize. In this case, each collection is a string. Here are the execution steps:
- Execute on all strings
upperCased()
Function, which is similar to:
[" ABC ", "def", "ghi"]. Map {$0.uppercased() }
Copy the code
Output:
Output: [" ABC ", "DEF", "GHI"]Copy the code
- Flattening collections into a collection.
output: ["A"."B"."C"."D"."E"."F"."G"."H"."I"]
Copy the code
Note: In Swift3 flatMap can also automatically filter nil values. However, this feature has been deprecated. This is now done using compactMap, which we'll cover later in this article.
Now you know what flatMap does.
Flatmap on array
letArrs = [[1,2,3], [4, 5, 6]let flat1 = arrs.flatMap { return $0 }
print(flat1) //[1, 2, 3, 4, 5, 6]
Copy the code
Flatmap on array of dictionaries
Because the returned array after being flattened contains elements of type tuple. So we have to convert to dictionaries.
let arrs = [["key1": 0."key2": 1], ["key3": 3."key4": 4]]
let flat1 = arrs.flatMap { return $0 }
print(flat1)
//[(key: "key2", value: 1), (key: "key1", value: 0), (key: "key3", value: 3), (key: "key4", value: 4)]
var dict = [String: Int]()
flat1.forEach { (key, value) in
dict[key] = value
}
print(dict)
//["key4": 4."key2": 1, "key1": 0."key3": 3)Copy the code
Flatmap on set
Flatmap by filtering or mapping
We can use a flatMap to flatten a two-dimensional array into a one-dimensional array. The closure of flatMap takes a set of parameters, and we can also perform filter Map Reduce operations in the closure.
let collections = [[5, 2, 7], [4, 8], [9, 1, 3]]
let onlyEven = collections.flatMap { (intArray) in
intArray.filter({ $0% 2 == 0})}print(onlyEven) //[2, 4, 8]
Copy the code
A simplified version of the above code:
let onlyEven = collections.flatMap { $0.filter { $0% 2 == 0}}Copy the code
Chain: (Map + filter + reduce)
We can chain-call higher-order functions. Don’t link too much, or execution will be slow. I can’t execute the following code in playground.
let arrayOfArrays = [[1, 2, 3, 4], [5, 6, 7, 8, 4]]
let sumOfSquareOfEvenNums = arrayOfArrays.flatMap{$0}.filter{$0 % 2 == 0}.map{$0 * $0}.reduce {0, +}
print(sumOfSquareOfEvenNums) // 136 // This workslet SquareOfEvenNums = arrayOfArrays.flatMap{$0}.filter{$0 % 2 == 0}.map{$0 * $0}
let sum = SquareOfEvenNums.reduce(0 , +) // 136
Copy the code
CompactMap
Returns a non-empty array after iterating through the mapping of each element in the collection.
let arr = [1, nil, 3, 4, nil]
let result = arr.compactMap{ $0 }
print(result) //[1, 3, 4]
Copy the code
It does the same thing for sets as it does for arrays.
let nums: Set = [1, 2, nil]
let r1 = nums.compactMap { $0 }
print(r1) //[2, 1]
Copy the code
In the case of a Dictionary, it doesn’t do anything, it just returns an array of tuples. So we need to use the compactMapValues function. (This function is available on Swift5)
let dict = ["key1": nil, "key2": 20]
let result = dict.compactMap{ $0 }
print(result) //[(key: "key1", value: nil), (key: "key2", value: Optional(20))]
let dict = ["key1": nil, "key2": 20]
let result = dict.compactMapValues{ $0 }
print(result)
Copy the code
Tip
let arr = [1, nil, 3, 4, nil]
let result = arr.map { $0 }
print(result) //[Optional(1), nil, Optional(3), Optional(4), nil]
Copy the code
Using Map looks like this.
conclusion
Well, this is the end of the article.
We want to use higher-order functions as much as possible:
- It can improve your Swift skills
- More readable code
- More functional