Swift4 Day98:map、forEach、compactMap、filter、reduce、sorted、reverse

Functional programming:higher-order function note

Alice
Daily Swift

--

map()

loop Array 中的每個 item 對它進行操作 用

  • 用 map 改寫 for 迴圈算字的長度
  • Array 中的各個 item uppercased、lowercased 或轉成 String
  • 或用 Ternary operator r (a ? b : c) 篩選 Array 內容超過 85 分 Pass 小於的 Fail 與 45~55 間的為 “Within average” 反之 ”Outside average”
  • 用 map 搭配 sqrt 開根號
  • map 是會保留 nil 的(補充:反之 compactMap / flatMap 則是不會保留 nil ),所以會回傳 Optional(100) 而不是 100
let i: Int? = 50let j = i.map { $0 * 2 }// Optional(100)

利用 map 這個特性改寫下面的

let username2 = aliceBirthday(in: 1995)let formattedUsername2: Stringif let username = username2 {formattedUsername2 = "Welcome, \(username)!"} else {formattedUsername2 = "Unknown user"}

forEach()

感覺跟 map 很像,loop Array 中的每個 item。

最大的不同在於 map 會 return 一個新的 Array,forEach 則不會 return 任何東西。如果 loop Array 後需要儲存為新的Array 使用 map,如果只是使用 Array item 內容使用 forEach 會是比較好的選擇。

flatMap

let numbers = [[1, 2], [3, 4], [5, 6]] 
let joined = Array(numbers.joined()) //[1, 2, 3, 4, 5, 6]

你可以用 joined() 讓兩個 Array 合在一起,而 flatMap 則是結合 map 與 joined()的好方法

但如果 Array 中有 nil Swift 4.1後系統就會建議使用 compactMap

compactMap()

map 跟 compactMap 最大的差別是 map 會回傳 nil 跟optional ,compactMap則會濾掉 nil,直接回傳有值的部分。

可以用這個方法濾掉轉型失敗的 item ,例如以下 Stirng 轉 int 的應用場景

或用這種方式找出 subviews 中的 UILabel

let labels = view.subviews.flatMap { $0 as? UILabel }

filter()

可以找出除以三整除的數字

let fibonacciNumbers = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]let number = fibonacciNumbers.filter { $0 % 3 == 0 }//[3, 21]

搭配 hasPrefix 篩選出想要的 item 組合

let names = ["12", "11", "2312", "32131", "9998"]let result = names.filter { $0.hasPrefix("1") }print(result)//"["12", "11"]\n"

或搭配 contains 比對 String 中包含的 Array item

import Foundationlet words = ["Alice", "Crystal", "Nia"]let input = "Alice in wonderland"let result = words.filter { input.contains($0) }print(result)//["Alice"]

也可以用 Filter 做出類似 compactMap 的效果把 nil 篩選掉,但不同在於會回傳 Optional。

let words = ["Alice", "Crystal", nil]let result = words.filter { $0 != nil }print(result)//[Optional("Alice"), Optional("Crystal")]

用這個方法 filter Array 中的 value

let scores = ["Alice": 100, "Nia": 95, "Crystal": 90, "Annie": 85, "Joy": 60]let goodScores = scores.filter { $1 > 85 }print(goodScores.keys) //["Alice", "Nia", "Crystal"]

reduce()

可以用來相加數字

let scores = [100, 90, 95]let sum = scores.reduce(0, +) // 從 0 開始 對每個數相加//285let sum2 = scores.reduce(25, +)//310

或直接相加 String

let result = scores.reduce("") { $0 + String($1) }print(result) //1009095

或是用 reduce 算 Array 中的個數 count 不使用 for 迴圈

let names = ["Alice", "Crystal", "Annie"]let count = names.reduce(0) { $0 + $1.count } //17

算是不是所有字數都<4 ,只要其中一個字串<4就會是 false ,存在 $0 後繼續跟後面的字串對比

let names = ["Nia", "Alice", "Crystal"]let longEnough = names.reduce(true) { $0 && $1.count > 4}print(longEnough) //false

也可以用來找最長的字串

let names = ["Nia", "Alice", "Crystal"]let longest = names.reduce("") { $1.count > $0.count ? $1 : $0 }print(longest)// Crystal

或用 max 做相同的事,而 reduce 從”” 開始所以永遠有值, max 則需要

unwrap

let longest = names.max { $1.count > $0.count }print(longest)//Optional("Crystal")

Reducing a multi-dimensional array

合併 Array 的方法現在有三種了

let numbers = [ [1, 1, 2], [3, 5, 8], [13, 21, 34] ]let flattened: [Int] = numbers.reduce([]) { $0 + $1 }let flattened2 = numbers.flatMap { $0 }let flattened3 = Array(numbers.joined())print(flattened, flattened2, flattened3)//[1, 1, 2, 3, 5, 8, 13, 21, 34] [1, 1, 2, 3, 5, 8, 13, 21, 34] [1, 1, 2, 3, 5, 8, 13, 21, 34]

sorted()

用 string 去會去 sort character ,所以 10230 會比 11 排的還要前面,當比對10 與 11,就會把 10230 放在前面

let scoresString = ["1","11", "21", "32", "11111", "10230"]let sortedString = scoresString.sorted()print(sortedString)//["1", "10230", "11", "11111", "21", "32"]

而如果你想照數字大小排的話就要轉成 Int

let scoresInt = scoresString.compactMap { Int($0) }let sortedInt = scoresInt.sorted()print(sortedInt)//[1, 11, 21, 32, 10230, 11111]

也可以結合 compactMap ,用更簡潔的方式轉成 Int 再排序

let scoresInt = scoresString.compactMap { Int($0) }.sorted()
//[1, 11, 21, 32, 10230, 11111]

試著用更複雜的資料去 sorted,排序 age

struct Person {var name: Stringvar age: Int}let alice = Person(name: "Alice", age: 18)let crystal = Person(name: "Crystal", age: 25)let nia = Person(name: "Nia", age: 22)let annie = Person(name: "Annie", age: 2)let people = [alice, crystal, nia, annie]let sortedPeople = people.sorted { $0.age < $1.age }print(sortedPeople)//[__lldb_expr_1.Person(name: "Annie", age: 2), __lldb_expr_1.Person(name: "Alice", age: 18), __lldb_expr_1.Person(name: "Nia", age: 22), __lldb_expr_1.Person(name: "Crystal", age: 25)]

reverse()

把 Array 的順序倒換

var people = ["alice", "crystal", "nia", "annie"]people.reverse()//["annie", "nia", "crystal", "alice"]

整理一些常用的高階函數🙋‍♀️

--

--