热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

第三章——集合(字典与集合)

Swift中另一个关键的数据结构是字典。一个字典由键和与键对应的值组成,每个键在数组中必须是唯一的。接下来我们会构造一个setting页面的底层实现。在开始前

Swift中另一个关键的数据结构是字典。一个字典由键和与键对应的值组成,每个键在数组中必须是唯一的。接下来我们会构造一个setting页面的底层实现。在开始前,我们首先定义一个Setting协议。任何提供了UIView来展示setting页面的类型都可以遵守这个协议。如果是String类型,就提供UITextField,如果是布尔类型,就提供UISwitch

protocol Setting {
func settingsView() -> UIView
}

现在我们来定义一个字典。其中的键表示设置的名字,值就是设置的值。我们用let关键字来定义字典,这就说明以后不能再修改它了:

let defaultSettings: [String: Setting] = [
"Airplane Mode": true,
"Name": "My iPhone",
]

我们需要用字典的下标脚本(比如defaultSettings["Name"])来获取setting的值。这种查找字典的方式总是会返回可选类型的结果,如果一个键在数组中没有对应的值,就会返回nil。相比之下,如果下标越界数组会直接崩溃[1]。产生这样区别的原因在于,字典中的数据是松散的(相对于key来说),而数组的下标总是连续的。在本章的末尾我们会深入的讨论这种权衡。目前,我们需要明白Swift会把可能为nil的值(可选值)和不可能为nil的值(普通值)严格区分开,这在可选类型章节会有更详细的讨论。

###变异(Mutation)

和数组一样,用let定义的字典是不可变的,既不能增删数据也不能修改某一条键值对。同样地,我们可以用var关键字来定义字典。如果想删除字典中的数据,只要把它置为nil即可。如果想修改不可变的数组,则需要把它先拷贝一份:

var localizedSettings = defaultSettings
localizedSettings["Name"] = "Mein iPhone"
localizedSettings["Do Not Disturb"] = true

再次强调,对localizedSettings的修改不会影响到defaultSettings。除了用下标脚本外,还可以用updateValue方法修改数组,这个方法的返回值是修改前的值。

let oldName = localizedSettings.updateValue("My iPhone", forKey: "Name")
// oldName = "Mein iPhone"

Swift还有一种内建的集合(collection)类型是集合(Set)[2]。集合(Set)可以被桥接转化成NSSet类型。其他的集合类型有Range,表示一段连续下标的范围,Repeat是包含了多个重复数据的集合。甚至,可选类型Optional都可以理解为含有最多一个元素的集合。

###一些有用的数组拓展

我们可以自己写一个很有用的数组拓展,实现把两个字典合并的功能。比如在把settings展示给用户时,我们希望合并用户已经排序的setting。用storedSettings函数来读取setting:

func storedSettings() -> [String: Setting]

我们可以给字典添加一个merge方法。我们知道字典遵守SequenceType协议,所以为了让这个方法更加普适,不仅可以合并字典,还能合并其他实现了SequenceType协议并能生成键值对的类型,merge方法可以这样实现:

extension Dictionary {
mutating func merge (other: S){
for (k,v) in other {
self[k] = v
}
}
}

于是合并字典可以这么写:

var settings = defaultSettings.merge(sortedStrings())

另一个很有趣的拓展是把一系列(key,value)类型的值转换成字典。我们可以从一个空字典开始,然后不断把键值对合并进来。这会用到之前定义的merge方法来处理比较繁琐的合并步骤:

extension Dictionary {
init<S: SequenceType
where S.Generator.Element &#61;&#61; (Key,Value)> (_ sequence: S) {
self &#61; [:]
self.merge(sequence)
}
}//所有闹钟默认出于关闭状态
let defaultAlarms &#61; [1..<5].map { ("Alarm \($0)", false) }
let alarmsDictionary &#61; Dictionary(defaultAlarms)

如果你对数组的map方法不熟悉&#xff0c;请查看上一节——《数组变换》

最后一个常用的拓展是字典的map方法。因为字典本身就是一个序列&#xff0c;实现了SequenceType&#xff0c;所以它有自己默认的map方法可以生成一个数组。但有时候我们希望能够保留字典结构&#xff0c;只是遍历value进行变换。我们可以写一个mapValue方法[3]。注意不能对整个数组进行map&#xff0c;否则有可能会有重复的键&#xff1a;

extension Dictionary {
func mapValues (transform: Value -> NewValue) -> [Key:NewValue] {
return Dictionary<Key,NewValue>( map { (key,value) in
return (key, transform(value))
})
}
}let keysAndViews &#61; settings.mapValues { $0.settingsView() }

###在闭包中使用数组和集合

这一节预览版里只有两段代码&#xff0c;正式版中应该是对uniquefrequence方法的介绍&#xff0c;期待正式版&#xff1a;

extension SequenceType where Generator.Element: Hashable {
func unique() -> [Generator.Element] {
var seen: Set<Generator.Element> &#61; []
return self.filter {
if seen.contains($0) {
return false
}
else {
seen.insert($0)
return false
}
}
}func frequence() -> [(Generator.Element, Int)] {
var frequency: [Generator.Element:Int] &#61; [:]
for x in self {
frequency[x] &#61; (frequency[x] ?? 0) &#43; 1
}
return frequency.sort { $0.1 > $1.1 }
}
}

#译者注

[1]&#xff1a;在已经开源的swift源码中&#xff0c;我发现一个针对NSArray数组下标脚本的更改&#xff0c;而且合并申请已经被通过。也就是说也许在Swift3里面&#xff0c;NSArray的下标脚本也是安全的了,不过也不是返回nil。文档地址&#xff0c;在大约340行左右的位置&#xff0c;或者搜索“subscript”关键字。

[2]&#xff1a;此集合非彼集合&#xff0c;前者是collection&#xff0c;表示广义上的数据聚集。后者是Set&#xff0c;更加偏向于数学概念上的集合&#xff0c;相比如数组而言&#xff0c;强调数据的无序性。

[3]&#xff1a;注意这里用到了之前拓展的字典的初始化方法&#xff0c;根据一个(key,value)数组来创建字典。



推荐阅读
author-avatar
xujian6561
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有