When we do function expressions, there are too many choices to operate on a function, which we call a higher-order function in the Swift feature.

Those who have learned Rxswift must be familiar with it. These Rx operators or swift higher-order functions simplify the internal process of function conversion to a certain extent, and their particularity and ease of use have also become popular in the development of daily use. I hope my understanding of the following higher-order functions is correct.

When we first learn Swift syntax, we can see many functions that we haven’t seen in OC, such as sort and sorted

The evolution of syntax towards Swift5 allowed better methods to remain, and to become sorted, map, compcatMap, reduce, as is common today

By using higher-order functions, different mapping results are generated for the elements in the function, and new element result sets are finally obtained through combination, sorting, filtering and separation. In order to be familiar with and easy to understand as soon as possible, I also separate scene type one from scene type one.

Sequence of scenario 1

A mutable array is arranged in ascending or descending order

Var nums = [11, 3, 3, 4, 5, 6, 5, 1] nums.sort() print nums: Ascending [1, 3, 3, 4, 5, 5, 6, 11] descending [11, 6, 5, 5, 4, 3, 3, 1)Copy the code

An immutable array is arranged in ascending or descending order

Sorted (by: >)// Print arr in descending order: Print newArr: print newArr: print newArr: print newArr: print newArr: print newArr: print newArrCopy the code

Dictionary order

Sorted {$0.0 > $1.0} print key: descending order [(key: "word", value: 555), (key: "idt", value: 0), (key: "idt", value: 0) "Id ", value: 123)] print value: descending [(key: "word", value: 555), (key: "id", value: 123), (key: "idt", value: 0)] print value: descending [(key: "word", value: 555), (key: "id", value: 0)]Copy the code

The ids of a set of data models are arranged in ascending order

struct GameInfo { var name: String? var id: Int? var title: String? var date: String? } var data = [GameInfo(name: "yyds", id: 0, title: "1632366361"), GameInfo(name: "letgo", id: 2, title: Date: "1632366395"), GameInfo(name: "cc", id: 1, title: "Naruto ", date: "1632366995") ] let max = data.sorted { (mod0, mod1) -> Bool in mod0.id! < mod1.id! } print newData: [[" date: "1632366361," id ": 0," name ":" yyds ", "title" : "king glory"], [" id ": 1," title ":" naruto ", "date" : 1632366995, "name" : "cc"], [" id ": 2," title ":" king glory ", "name" : "letgo", "date" : 1632366395]]Copy the code

Sort: Allows sorting of simple to complex data structures. Sort not returned, modified on the original array; Sorted returns an ordered array without modifying the original array

Scenario 2 Search

Count the score of a certain interval and get the number of people

let number = [10, 30, 50, 0 , 90, 91, 100, 98, 0] let excellent = number.filter{ $0 <= 100 && $0 >= 90 } let good = number.filter{ $0 == 100 } let failed = Number. Filter {$0 < 60} let zero = number. Filter {$0 == 0} let fraction = excellent. Map {"($0) cent "} let zero_fraction = failed. Map {"($0)分"} print: ["90 marks ", "91 marks ", "100 marks ", "98 marks "] [" 100 marks ", "91 marks ", "100 marks ", "98 marks "] ["10 marks ", "30 marks ", "50 marks ", "0 marks ", "0 marks"Copy the code

NavigationController stack viewController, and change the corresponding title, backgroundColor

navigationController? .viewcontroller. filter{(vc) -> Bool in vc.title == ""}. Map {$0. Title =" "$0.Copy the code

Filter: Used to filter out the elements that meet the conditions in the array and generate a new array. It can also map the elements in the new array based on the filter criteria

Scenario 3 Calculation and transformation

Aunt Zhang took five yuan and Uncle Chen took twenty yuan to buy vegetables, they all want to buy carrots and cucumbers, and need to leave a dollar to go home by car, please ask Aunt Zhang and Uncle Chen how much each bought (carrot 1 yuan, cucumber 2 yuan)

Let nums = [" nums ": 5, "nums ": 20] let money = nums.map { (person) -> String in let remaining = person.value - 1 let all = remaining / 3 var carrot = 0 Var cucumber = 0 if remaining % 3 == 1 {carrot = 1}else if remaining % 3 == 2 {carrot = 2} return "(person.key (carrot) (carrot) (carrot) (carrot) (carrot) (carrot) (carrot)Copy the code

Solve the multi-layer collection effect

let arr = [[1, 2, 3],[5, 5, 5],[3, 5, 7]]
let newarr = arr.flatMap{ $0 }
​
print:
[1, 2, 3, 5, 5, 5, 3, 5, 7]
Copy the code

Create layers

let arr = [1, 2, 3, 4] let abc = aar.compactMap { Array(repeating: $0, count: $0)} [[1], [2, 2], [3, 3, 3], [4, 4, 4]Copy the code

Map: returns a generic collection of arrays by mapping the elements in the collection; CompactMap filters out elements that fail to convert to nil, the rest is the same as map; FlatMap changes the hierarchy of the original collection to merge elements, and can be viewed as

So, after Swift4.1, compactMap is used when the closure returns optional, and generally map is used.

A more powerful scenario: Reduce

I’ve been in a situation where I needed to take cookies in the evaluateJavaScript method and convert them from the original non-standard String to the standard jsonString format, and here’s what I did

webView.evaluateJavaScript("document.cookie") { (cookie, error) in let cook = cookie as? String ?? "" let array = cook.replacingOccurrences(of: " ", with: "").components(separatedBy: ";" ) let units = array.reduce(into: [String: String]()) { result, key in let arr = key.components(separatedBy: "=") let key = arr[0] var value: String? value = arr[1] result[key] = value ?? "" } guard let pt_cookies = units.toJSONString() else { return } print(pt_cookies) }Copy the code

There are no complex operations, no complex extraction of character arrays, no frequent operations in dictionaries, and the conversion process is easily implemented internally. It is the use of Reduce that satisfies all our needs and aspirations for higher-order functions.

Two functions of reduce

@inlinable public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
​
@inlinable public func reduce<Result>(into initialResult: __owned Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
Copy the code

Mainly nextPartialResult: (1) the Result, the Element) and updateAccumulatingResult: (inout Result, Element).

One is to assign the result of traversing the closure to a temporary variable, and the other is to take the address of the variable as the first argument to the closure after traversing, and to execute the closure is to operate on the value of the variable.

Map is implemented using Reduce

let list = [1, 2, 3]
let s = list.reduce(into: String()) { (red, key) in
  red.append("(key)")
}
​
print:
"123"
Copy the code

Use Reduce to implement filter

let nums = [11, 3, 3, 4, 5, 6, 5, 1]
let reduce = nums.reduce(into: [Int]()) { (result, key) in
  if key > 4 {
    result.append(key)
  }
}
​
print:
[11, 5, 6, 5]
Copy the code

Use the reduce chain combination to implement sorted

let r = nums.sorted(by: >).reduce(into: [Int]()) { (result, key) in
  if key > 4 {
    result.append(key)
  }
}
Copy the code

In the reduce function used above, into represents the initial type (the default can be set), the first argument in the closure meta-ancestor is the container, and the second argument is the elements that the original sequence traverses one by one. It helps me effectively express my intentions in limited code, making the internal structure more straightforward.

The effect of higher-order functions on addresses

var arr = [1, 2, 3, 4]
let b1 = withUnsafePointer(to: &arr) {$0}
arr.map{ $0 }
let b2 = withUnsafePointer(to: &arr) {$0}
var filter = arr.filter{ $0 < 4 }
let b3 = withUnsafePointer(to: &filter) {$0}

print:
b1 0x00007ffee289c300
b2 0x00007ffee289c300
b3 0x00007ffee3a392c0
Copy the code