Swift 的 Map、Filter、Reduce等高阶函数,以及$的含义

引言

从OC转到Swift已经有接近半年了,但是很多地方都是用的最笨最基础的写法来完成,虽然代码没问题,功能能实现,不过对于想提升自己的你来说,还是得找个途径来优化自己的代码,自己维护也轻松,别人看着也舒适。正所谓不积跬步无以至千里,自己也正在一点一滴的积累,在网上看着大神们的写法,敲一遍不代码记得住,所以写篇文章来记录知识。


在说高阶函数之前,我们需要理解一个符号: $

swift 自动为闭包提供参数名缩写功能,可以直接通过$0$1,来分别表示闭包中的第一个参数第二个参数,并且对应的参数类型会根据类型来进行判断。

  • 不使用$0$1这些来代替
let numbers = [1,2,5,4,3,6,8,7]
sortNumbers = numbers.sorted(by: { (a, b) -> Bool in
    return a > b
})
print(sortNumbers)  // [8,7,6,5,4,3,2,1]
  • 使用$0$1
let numbers = [1,2,5,4,3,6,8,7]
let sortNumber = numbers.sorted(by: {$0 < $1})
print(sortNumber)   // [1,2,3,4,5,6,7,8]

可以发现,使用$0$1的话,参数类型可以自动判断,并且关键字in也可以省略,也就是只用写函数体就可以了


Map

map函数能够被数组调用,它接受一个闭包作为参数,作用于数组中的每个元素。闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组

简单来说,就是把数组里面的值给替换了,不用写for循环

1. 比如,现在有一个Int类型的数组,需要将数组内的所有数字与自身相加
// 常规写法
let numbers = [1,2,3]
var sumNumbers:Array<Int> = Array() 
for var number in numbers {
    number += number
    sumNumbers.append(number)
}
print(sumNumbers) // [2,4,6]
// 通过map简化后的写法

// 1.可以看到我们甚至可以不再定义可变的数组直接用不可变的就可以
let sumNumber1 = numbers.map { (number) -> Int in
    return number + number
}
// 2.可以省了return,提出一个问题,在下方解释
let sumNumbers3 = numbers.map { number in number + number }
print(sumNumbers3)

// 3.最终简化写法,可以省略return
let sumNumber4 = numbers.map { $0 + $0 }
print(sumNumber4)  // [2,4,6]

这里有个小问题,就是发现在Playground里面执行,1写的法是有return的执行了3次,而2写法没有return却执行了4次,很多人理解为循环了3次或是4次,实则不然,这里的意思为本行代码执行了多少次,不信你可以在2写法的in后的代码开始换行,换为正常格式,一目了然

2. Map函数返回数组的元素类型,不一定要与原来数组相同

let fruits = ["apple", "banana", "orange", ""]
// 这里数组中存在一个""的字符串 为了后面来比较 map 和 flatMap
let counts = fruits.map { (fruit) -> Int? in
    let strCount = fruit.count
    guard strCount > 0 else {
        return nil
    }
    return strCount
}
print(counts) // [Optional(5), Optional(6), Optional(6), nil]

3. Map还能返回判断数组中的元素是否满足某种条件的Bool值数组

let array = [1,2,3,4,5,6]
let isEven = array.map { $0 % 2 == 0 }
print(isEven)  // [false, true, false, true, false, true]

FlatMap

flatMap 与 map 不同之处:

  1. flatMap返回后的数组中不存在 nil 同时它会把Optional解包;
  2. flatMap 还能把数组中存有数组的数组 一同打开变成一个新的数组;
  3. flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积。

1. flatMap返回后的数组中不存在nil 同时它会把Optional解包

let fruits = ["apple", "banana", "orange", ""]  // 同上面的数组
let counts = fruits. flatMap { (fruit) -> Int? in
    let strCount = fruit.count
    guard strCount > 0 else {
        return nil
    }
    return strCount
}
print(counts) // [5,6,6]

2. flatMap 能把数组中存有数组的数组 一同打开变成一个新的数组,看代码秒懂

let array = [[1,2,3], [4,5,6], [7,8,9]]

// 如果用map来获取新的数组
let arrayMap = array.map { $0 }
print(arrayMap)  // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

// 用flatMap
let arrayFlatMap = array.flatMap { $0 }
print(arrayFlatMap)   // [1, 2, 3, 4, 5, 6, 7, 8, 9]

3. flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积

// 这种情况是把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积
let fruits = ["apple", "banana", "orange"]
let counts = [1, 2, 3]
let fruitCounts = counts.flatMap { count in
    fruits.map { fruit in
        // title + "\(index)"
        // 也可以返回元组
        (fruit, count)
    }
} 
print(fruitCounts) // [("apple", 1), ("banana", 1), ("orange", 1), ("apple", 2), ("banana", 2), ("orange", 2), ("apple", 3), ("banana", 3), ("orange", 3)]
// 这种方法估计用的很少 可以算是一个 flatMap 和 map 的结合使用吧

Filter

filter 可以取出数组中符合条件的元素 重新组成一个新的数组,可以理解为过滤

filter可以很好的帮我们把数组中不需要的值都去掉 这个很赞
let numbers = [1,2,3,4,5,6]
let evens = numbers.filter { $0 % 2 == 0 }
print(evens)  // [2, 4, 6]
取出数组中大于22的值
// filter 筛选数组中大于22的元素,返回值为新数组
let intArr2 = [21,23,25]
let filtereArr = intArr2.filter{ $0 > 22 }
print(filtereArr)

Reduce

map,flatMap和filter方法都是通过一个已存在的数组,生成一个新的、经过修改的数组。然而有时候我们需要把所有元素的值合并成一个新的值 那么就用到了Reduce

1. 比如我们要获得一个数组中所有元素的和

let numbers = [1,2,3,4,5]
// reduce 函数第一个参数是设定的初始化值,这里设置为0
let sum = numbers.reduce(0, { $0 + $1 })
// 这里我写下完整的格式
let sum1 = numbers.reduce(0) { total, num in
    total + num
}
// 这里还能再次简写
let sum = numbers.reduce(0, +)
print(sum)  // 15

2. 合并成的新值不一定跟原数组中元素的类型相同

let numbers = [1,5,1,8,8,8,8,8,8,8,8]
// reduce 函数第一个参数是返回值的初始化值
let tel = numbers.reduce("", { "\($0)" + "\($1)" }) 
print(tel)  // 15188888888

3. ruduce 还可以实现 map 和 filter 并且时间复杂度,从O(n*n)变为O(n)

extension Array {
    func mMap<U> (transform: Element -> U) -> [U] {
        return reduce([], combine: { $0 + [transform($1)] })
    }
    func mFilter (includeElement: Element -> Bool) -> [Element] {
        return reduce([]) { includeElement($1) ? $0 + [$1] : $0 }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容