你可能不知道的 Swift 开发小技巧——Pt.2

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战


往期文章: 你可能不知道的 Swift 开发小技巧——Pt.1


对数组进行分组

假如有一组文章,要把这些文章按照 category 进行分组,可以利用 Pt.1中提到的,给字典的某个 key 设置默认值的技巧,实现如下:

struct Article {
    let title: String
    let category: String
}

let articles = [
    Article(title: "标题1", category: "Swift"),
    Article(title: "标题2", category: "Swift"),
    Article(title: "标题3", category: "Objective-C"),
    Article(title: "标题4", category: "Objective-C"),
]

var groupByCategory = [String : [Article]]()
for article in articles {
    groupByCategory[article.category, default: []].append(article)
}
print(groupByCategory)
复制代码

实际上我们可以使用 Dictionary 的内置初始化方法来简化上面的 for 循环:

let groupByCategory = Dictionary(grouping: articles, by: {$0.category})
print(groupByCategory)
复制代码

检查所有集合项是否满足条件

假如有这么需求:给定一组表示年龄的整型数组,求他们的平均年龄。计算总和,除以数量,so easy~

然而并不是每个整数都能表示年龄,因为传入的值有可能小于等于 0。对于输入参数:年龄数组,是不可控的。因此遇到不符合实际的值,需要抛出错误。通常会这么实现:

func findAverage(ages: [Int]) -> CGFloat {
    var isAgeValid = true
    for age in ages {
        if age <= 0 {
            isAgeValid = false
            break
        }
    }
    guard isAgeValid else {
        fatalError("所有的年龄必须大于 0")
    }
    let sum = ages.reduce(0, +)
    let count = ages.count
    return CGFloat(sum) / CGFloat(count)
}

findAverage(ages: [22, 25, 26, 28, 30])
复制代码

我们可以使用 Array 的方法:allSatisfy(_:)来简化 for 循环代码:

func findAverage(ages: [Int]) -> Double {
    let isAgeValid = ages.allSatisfy { $0 > 0 }
    guard isAgeValid else {
        fatalError("所有的年龄必须大于 0")
    }
    let sum = ages.reduce(0, +)
    let count = ages.count

    return Double(sum) / Double(count)
}

findAverage(ages: [22, 25, 26, 28, 30])
复制代码

保留 Struct 的默认初始化器

大家都知道,Swift 中的 Struct 是值类型,自带一个成员初始化器。

不知道大家有没有遇到这样的情况,需要自定义 Struct 的初始化器,又想保留默认的初始化器,但是自定义后,默认的初始化器就消失了。

先看下默认的初始化器:

image.png

自定义初始化器后,默认初始化器就消失了:

image.png

想同时保留自定义的和默认的,就得祭出 extension 大法了:

image.png


过滤数组中的 nil

提到过滤,第一反应可能就是 filter(_:)

let x = [1, nil, 2, 4]

let y = x.filter { $0 != nil }
复制代码

虽然也可以,但不不是那么好。上面的 x 的类型 [Int?],y 也是。有什么问题?问题是,我们已经明确知道 y 里面不会包含 nil 值了,但编译器不知道,所以在遍历 y 时,依然需要对其中的元素进行解包:

let x = [1, nil, 2, 4]
let y = x.filter { $0 != nil }
for item in y {
    print("item is \(item!)")
}
复制代码

更好的做法是使用 compactMap(_:)

let x = [1, nil, 2, 4]

let y = x.compactMap { $0 }
复制代码

这时候 x 是 [Int?] 的类型,y 是 [Int] 的类型。


zip

假如需要同时遍历两个数组,你会怎么做?在我知道 zip 之前,我通常都是这么做:

let array1 = ["title1", "title2", "title3", "title4"]
let array2 = ["value1", "value2", "value3", "value4", "value5"]

let count = min(array1.count, array2.count)
for i in 0..<count {
    let title = array1[i]
    let value = array2[i]
}
复制代码

再来看看 zip 的用法,zip 可以组合两个序列,而且会进行边界检查:

for (title,value) in zip(array1, array2) {
    print("\(title)---\(value)")
}

//输出结果
title1---value1
title2---value2
title3---value3
title4---value4
复制代码

结语

  • 使用 Dictionary 的 init(grouping:by:) 方法对数组进行分组
  • 使用 Array 的 allSatisfy(_:) 方法检查所有集合项是否满足条件
  • 通过 extension 添加自定义初始化器从而保留默认初始化器
  • 使用 compactMap(_:) 过滤数组中的 nil
  • 使用 zip 组合两个集合

往期文章: 你可能不知道的 Swift 开发小技巧——Pt.1

以上就是平时在项目中使用比较多的 Swift 小技巧啦,我也会继续总结更多的小技巧和大家进行分享~

如果大家遇到以前没用到过的,不妨动手试试吧~

猜你喜欢

转载自juejin.im/post/7031003650679898149