Ios开发Swiftswift开发技巧

Swift4字典和集合的新特性

2017-10-13  本文已影响64人  骨灰级果粉

Swift4中的字典和集合在这些方面变得更好

写在前面

在最新版本的Swift中,dictionaries和sets新增了很多行为方法和初始化方法,让一些常见的任务变得异常简单。诸如组合、过滤和transform值操作可以用一步完成,让使用者可以写出更高效和简洁的代码。

本篇博客将使用杂货铺中的商品作为例子演示这些新功能。GroceryItem结构体,由名字和部门组成,作为本例的数据类型。

struct GroceryItem: Hashable {
    var name: String
    var department: Department

    enum Department {
        case bakery, produce, seafood
    }

    static func ==(lhs: GroceryItem, rhs: GroceryItem) -> Bool {
        return (lhs.name, lhs.department) == (rhs.name, rhs.department)
    }

    var hashValue: Int {
        // Combine the hash values for the name and department
        return name.hashValue << 2 | department.hashValue
    }
}

// Create some groceries for our store:
let 🍎 = GroceryItem(name: "Apples", department: .produce)
let 🍌 = GroceryItem(name: "Bananas", department: .produce)
let 🥐 = GroceryItem(name: "Croissants", department: .bakery)
let 🐟 = GroceryItem(name: "Salmon", department: .seafood)
let 🍇 = GroceryItem(name: "Grapes", department: .produce)
let 🍞 = GroceryItem(name: "Bread", department: .bakery)
let 🍤 = GroceryItem(name: "Shrimp", department: .seafood)

let groceries = [🍎, 🍌, 🥐, 🐟, 🍇, 🍞, 🍤]

后面的例子将围绕着groceries数组展示。

用Key值对原数组进行分组

字典拥有了一个新的初始化函数,可以将一系列值按照Key值进行分组。下面展示使用该初始化方法根据GroceryItem的department进行分组的一个小例子。

<div align=center>



</div>

在老版本的Swift中,用户可以使用如下的代码完成上述任务。

// Swift <= 3.1
var grouped: [GroceryItem.Department: [GroceryItem]] = [:]
for item in groceries {
    if grouped[item.department] != nil {
        grouped[item.department]!.append(item)
    } else {
        grouped[item.department] = [item]
    }
}

这一过程需要使用type annotations、手动循环并且需要检查departement是否已经存在了。

在Swift4中,用户可以使用Dictionary(grouping:by)方法,仅需一行代码就可以达到上述效果。所要做的是传入一个闭包,该闭包返回数组每一项项对应的Key值即可。

// Swift 4.0
let groceriesByDepartment = Dictionary(grouping: groceries,
                                       by: { item in item.department })
// groceriesByDepartment[.bakery] == [🥐, 🍞]

最终的字典groceriesByDepartment对每个department都有唯一入口,而且该入口对应着GroceryItem相应的name。例如,使用.bakery作为入口,将返回[🥐, 🍞]数组。

获得字典值的数量

使用新的mapValues(_:)方法,用户可以方便的获得每个入口对应数组的长度。以上面例子中获取的groceriesByDepartment字典为例:

let departmentCounts = groceriesByDepartment.mapValues { items in items.count }
// departmentCounts[.bakery] == 2

因为字典有相同的key,只是值不同,所以可以不需要重新计算哈希值,从而使得调用mapValues(_:)方法比从头建立字典快很多。

从键值对建立字典

Swift4提供了两种方法给用户从键值对序列生成字典,一种方法允许key有重复,另一种不允许。

使用zip(::)函数可以将一些了键值组合起来。例如下面的代码就创立了一系列(String,GroceryItem)元组。

let zippedNames = zip(groceries.map { $0.name }, groceries)

zippedNames的每一项都是一个元组(tuple),第一项是("Apples", 🍎).因为name值是唯一的,下面的方法就可以创建一个字典,也是我们上面提到的不允许key值重复的方法。

<div align=center>



</div>

var groceriesByName = Dictionary(uniqueKeysWithValues: zippedNames)
// groceriesByName["Apples"] == 🍎
// groceriesByName["Kumquats"] == nil

当然,要使用该方法的前提是你可以确保key值是不重复的。否则会引起runtime error。

如果key值可能会重复,使用另一个方法:Dictionary(_:uniquingKeysWith:)。这个方法需要传入一个闭包来处理当key重复时的操作。闭包的第一个参数是key(键)对应的old value(值),而第二个对应的是新值。用户可以在闭包里写相应的逻辑,比如新值替代老值,或者将新老值合并。

let pairs = [("dog", "🐕"), ("cat", "🐱"), ("dog", "🐶"), ("bunny", "🐰")]
let petmoji = Dictionary(pairs,
                         uniquingKeysWith: { (old, new) in new })
// petmoji["cat"] == "🐱"
// petmoji["dog"] == "🐶"

看上面的例子,dog对应了两个值。当方法处理到("dog", "🐶”)时,闭包的参数是 ("🐕”, "🐶”),而闭包的逻辑是返回第二个值,因此新值就代替了老值,最终的字典中,dog对应的值就是🐶。

筛选出特定的项

字典现在有了一个filter(_:)方法,返回值是满足条件的新字典(早期版本的swift返回的是一个数组)。方法传入的参数依然是一个闭包,如果某一项需要在返回值中出现,闭包返回true,否则返回false。

func isOutOfStock(_ item: GroceryItem) -> Bool {
    // Looks up `item` in inventory
}

let outOfStock = groceriesByName.filter { (_, item) in isOutOfStock(item) }
// outOfStock["Croissants"] == 🥐
// outOfStock["Apples"] == nil

上例中,isOutOfStock决定某一项该不该出现在返回值字典中。

使用默认值

字典现在提供了类似数组下标来获取和更新值,下面的代码定义了一个简单的购物篮,key是商品,value是商品的数量。

// Begin with a single banana
var cart = [🍌: 1]

因为某些key在字典中没有对应的值,因此你用key去获取值的时候,返回结果是optional的。

// One banana:
cart[🍌]    // Optional(1)
// But no shrimp:
cart[🍤]    // nil

可以使用??操作符将optinal值拆包为真实的数值,现在swift4提供了另一种解决方案(设置默认值),如果key对应的值存在,那么返回该值,否则返回默认值。如果key没有对应值,那么返回默认值。

// Still one banana:
cart[🍌, default: 0]    // 1
// And zero shrimp:
cart[🍤, default: 0]    // 0

甚至用下面的代码简化增加新item到购物车的过程。

for item in [🍌, 🍌, 🍞] {
    cart[item, default: 0] += 1
}

当循环处理到🍌时,检索到当前值,然后自增,放回到原字典中。当检索到🍞时,发现🍞现在并没有对应值,从而返回默认值0,自增为1,存储到字典中,下次检索的时候就变成了1.

合并两个字典到一个字典中

将两个字典合并也变得异常简单。swift4提供了merge(_:uniquingKeysWith:)方法来处理合并操作。和上面一样,需要传入一个闭包完成合并的逻辑,当两个字典拥有相同的key值时,由该闭包处理如何操作。

let otherCart = [🍌: 2, 🍇: 3]
cart.merge(otherCart, uniquingKeysWith: +)
// cart == [🍌: 5, 🍇: 3, 🍞: 1]

上面的代码将相同key对应的值相加作为新字典中的值。

如果不想原地合并,可以使用merging(_:uniquingKeysWith:)方法生成一个新字典。

And That’s Not All…

上面介绍的新特性并不是全部,限于篇幅,并没有完全介绍全。

和字典一样,集合也拥有了新的filter(:) 方法,返回的也是集合,而不是早起版本中的数组。字典和集合现在提供了暴漏现在capacity的方法:reserveCapacity(:),有了该方法,用户可以看到并控制他们的内部存储。

Reference

本文译自:https://swift.org/blog/dictionary-and-set-improvements/

上一篇 下一篇

猜你喜欢

热点阅读