Swift中的数组有一些高级API非常好用(用法类似JavaScript
)。
一、Array的常见操作
基础代码:
var arr = [1, 2, 3, 4]
print(arr) // 输出:[1, 2, 3, 4]
1.1. map(映射)
遍历每一个元素,元素在函数(闭包表达式)中处理完成后把返回值放到新的数组,返回一个新数组。
示例代码:
var arr2 = arr.map {
element -> Int in
return element * 2
}
print(arr2) // 输出:[2, 4, 6, 8]
// 简化写法
// var arr2 = arr.map { $0 * 2 }
// print(arr2) // 输出:[2, 4, 6, 8]
注意:
map
的闭包表达式返回值类型是泛型,也就意味返回新数组元素的类型可以是任意类型。
1.2. filter(过滤)
遍历每一个元素,满足函数(闭包表达式)条件(true)后把元素放到新的数组,返回一个新数组。
示例代码:
var arr2 = arr.filter {
element -> Bool in
return element % 2 == 0
}
print(arr2) // 输出:[2, 4]
// 简化写法
// var arr2 = arr.filter { $0 % 2 == 0 }
// print(arr2) // 输出:[2, 4]
1.3. reduce(累计)
第一个参数是初始值。第二个参数是闭包表达式(第一个参数是闭包返回值,首次传入的参数值是reduce
函数的第一个参数即初始值,第二个参数是数组元素,函数返回值作为下一次遍历时闭包表达式的第一个参数),数组遍历完成后把结果返回。
示例代码:
var arr2 = arr.reduce(0) {
(result, element) -> Int in
return result + element
}
print(arr2) // 输出:10
// 简化写法一
// var arr2 = arr.reduce(0) { $0 + $1 }
// print(arr2) // 输出:10
// 简化写法二
var arr2 = arr.reduce(0, +)
// print(arr2) // 输出:10
使用reduce
实现map
功能。
示例代码:
var arr2 = arr.reduce([]) { $0 + [$1 * 2] }
print(arr2) // 输出:[2, 4, 6, 8]
使用reduce
实现filter
功能。
示例代码:
var arr2 = arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 }
print(arr2) // 输出:[2, 4]
1.4. flatMap(元素平铺映射)
map
是返回一个泛型元素数组,返回值是什么,最终返回的数组元素就是什么。flatMap
返回元素值是Sequence
类型,而数组就是Sequence
类型,如果flatMap
传入的是一个数组,函数会把传入的数组所有元素平铺放到一个新的数组中返回。
示例代码:
// repeating:重复元素 count:重复次数
var arr2 = Array.init(repeating: 3, count: 4);
print(arr2) // 输出:[3, 3, 3, 3]
var arr3 = arr.map {
Array.init(repeating: $0, count: $0)
}
print(arr3) // 输出:[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
var arr4 = arr.flatMap {
Array.init(repeating: $0, count: $0)
}
print(arr4) // 输出:[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
1.5. compactMap(元素压缩映射)
compactMap
可以自动解包可选值,非nil
元素会被解包放到新的数组中。
示例代码:
var arr = ["123", "idbeny", "hello", "-20"];
var arr2 = arr.map { Int($0) }
print(arr2) // 输出:[Optional(123), nil, nil, Optional(-20)]
var arr3 = arr.compactMap { Int($0) }
print(arr3) // 输出:[123, -20]
1.6. lazy
直接使用map
会一次性把数组所有元素都遍历一遍。
示例代码:
let result = arr.map {
(i: Int) -> Int in
print("mapping \(i)")
return i * 2
}
print("begin---")
print("mapped \(result[0])")
print("mapped \(result[1])")
print("mapped \(result[2])")
print("end---")
/*
输出:
mapping 1
mapping 2
mapping 3
mapping 4
begin---
mapped 2
mapped 4
mapped 6
end---
*/
上面示例可以看出,使用数组result
前,数组已经被map
遍历完成。有没有可能用到result
时再去执行map
?可以的,使用lazy
。
示例代码:
let result = arr.lazy.map {
(i: Int) -> Int in
print("mapping \(i)")
return i * 2
}
print("begin---")
print("mapped \(result[0])")
print("mapped \(result[1])")
print("mapped \(result[2])")
print("end---")
/*
输出:
begin---
mapping 1
mapped 2
mapping 2
mapped 4
mapping 3
mapped 6
end---
*/
上面示例很明显看到只有用到result
时才去执行map
函数。
二、Optional的map和flatMap
map
对可选类型操作时,闭包表达式传入的是解包后的值,map
返回的类型也是可选类型。如果传入的可选类型是nil
,返回的也是nil
。
示例代码一:
var num1: Int? = 10
print(num1 as Any) // 输出:Optional(10)
var num2 = num1.map {
i -> Int in
print(i) // 输出:10
return i * 2
}
print(num2 as Any) // 输出:Optional(20)
var num3: Int? = nil
print(num3 as Any) // 输出:nil
var num4 = num3.map { $0 * 2}
print(num4 as Any) // 输出:nil
如果map
传入的是可选类型,闭包表达式中返回的也是可选类型,最终返回的是双重可选类型(会自动再次包装一层可选类型)。但是flatMap
发现返回的是可选类型时不会再次包装,如果不是可选类型就会再次包装一层。
示例代码二:
var num1: Int? = 10
var num2 = num1.map { Optional.some($0 * 2) }
print(num2 as Any) // 输出:Optional(Optional(20))
var num3 = num1.flatMap { Optional.some($0 * 2) }
print(num3 as Any) // 输出:Optional(20)
应用场景一:
下面代码中num2
和num3
是等价的。
var num1: Int? = 10
var num2 = (num1 != nil) ? (num1! + 10) : nil
var num3 = num1.map { $0 + 10 }
应用场景二:
下面代码中date1
和date2
是等价的。
var fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var str: String? = "2008-08-08"
var date1 = str != nil ? fmt.date(from: str!) : nil
var date2 = str.flatMap(fmt.date)
上面示例中为什么使用flatMap
,不使用map
?因为fmt.date
函数返回值是可选类型,使用map
会被再次包装一层可选类型。
应用场景三:
下面代码中str1
和str2
是等价的。
var score: Int? = 98
var str1 = score != nil ? "Score is \(score)" : "No score"
var str2 = score.map { "Score is \($0)" } ?? "No score"
??
前面如果是可选类型,后面是非可选类型,当可选类型值非nil
时会自动解包。
应用场景四:
根据name
找到对应Person
实例。getPerson1
和getPerson2
是等价的。
数组的first
函数可以根据索引找到第一个符合要求的对应元素,firstIndex
可以根据索引找到第一个符合要求的对应元素(这两个函数的闭包表达式入参都是数组元素,返回值都是Bool
类型,如果为true
就把对应索引或元素返回并且停止遍历。需要注意的是返回值都是可选类型)。
struct Person {
var name: String
var age: Int
}
var items = [
Person(name: "jack", age: 20),
Person(name: "rose", age: 21),
Person(name: "kate", age: 22)
]
func getPerson1(_ name: String) -> Person? {
let index = items.firstIndex { $0.name == name }
return index != nil ? items[index!] : nil
}
func getPerson2(_ name: String) -> Person? {
return items.firstIndex { $0.name == name }.map { items[$0] }
}
应用场景五:
字典转模型。下面示例中p1
和p2
的代码是等价的。
struct Person {
var name: String
var age: Int
init?(_ json: [String : Any]) {
guard let name = json["name"] as? String,
let age = json["age"] as? Int
else {
return nil
}
self.name = name
self.age = age
}
}
var json: Dictionary? = ["name" : "idbeny", "age" : 18]
var p1 = json != nil ? Person(json!) : nil
var p2 = json.flatMap(Person.init)