在Swift中用字典对数组元素进行分组

在Swift中用字典对数组元素进行分组

hudson 译 原文

想象一下,您有一个Device对象数组,并且您想按类别对它们进行分组,如下图所示:

你应该如何解决这个问题?

在Swift 5之前,最直接的方法是循环遍及数组中的每个设备,并手动将每个元素分配给其各自的类别。在Swift 5中,苹果引入了范型字典初始化器,以帮助开发人员只需一行代码即可处理这种情况。

init(grouping:by:)介绍

在Swift 5中,苹果引入了"分组"字典初始化器。根据文档,初始化器有以下定义:

创建一个新的字典,其键是给定闭包返回的分 组,其值是返回每个键的元素的数组。

为了更好地理解定义,让我们重温我们在本文开头看到的示例。

假设您有一个Device结构和一个Device对象数组,如下所示:

swift 复制代码
struct Device {
    let category: String
    let name: String
}

let deviceArray = [
    Device(category: "Laptop", name: "Macbook Air"),
    Device(category: "Laptop", name: "Macbook Pro"),
    Device(category: "Laptop", name: "Galaxy Book"),
    Device(category: "Laptop", name: "Chromebook"),
    Device(category: "Mobile Phone", name: "iPhone SE"),
    Device(category: "Mobile Phone", name: "iPhone 11"),
    Device(category: "Mobile Phone", name: "Galaxy S"),
    Device(category: "Mobile Phone", name: "Galaxy Note"),
    Device(category: "Mobile Phone", name: "Pixel")
]

要按类别对所有Device数组进行分组,我们可以通过创建一个新的字典,并向其初始化器提供源数组和一个闭包,该闭包返回需要进行分组的键。 看看下面的代码片段:

swift 复制代码
let groupByCategory = Dictionary(grouping: deviceArray) { (device) -> String in
    return device.category
}

/*
// Output of groupByCategory equivalent to:
[
    "Laptop": [
        Device(category: "Laptop", name: "Macbook Air"),
        Device(category: "Laptop", name: "Macbook Pro"),
        Device(category: "Laptop", name: "Galaxy Book"),
        Device(category: "Laptop", name: "Chromebook")
    ],
    "Mobile Phone": [
        Device(category: "Mobile Phone", name: "iPhone SE"),
        Device(category: "Mobile Phone", name: "iPhone 11"),
        Device(category: "Mobile Phone", name: "Galaxy S"),
        Device(category: "Mobile Phone", name: "Galaxy Note"),
        Device(category: "Mobile Phone", name: "Pixel")
    ]
]
*/

如您所见,我们只需要传递设备数组并将类别作为分组键返回,初始化器将为我们处理分组。

我们甚至可以通过使用Swift中的速记参数名称,将上述代码片段进一步简化为一行代码。

swift 复制代码
let groupByCategory = Dictionary(grouping: deviceArray) { $0.category }

很整洁,不是吗?

上面的示例演示了使用初始化器的最标准方式。然而,由于初始化器允许我们定义确定分组键的闭包,因此它比这灵活得多。

通过使用相同的deviceArray 数组 ,假设我们想将所有苹果产品组合在一起,我们实际上可以定义一个闭包,检查设备名称,并将所有设备分组为包含"Macbook"和"iPhone"的名称。

swift 复制代码
let groupByCategoryWithApple = Dictionary(grouping: deviceArray) { (device) -> String in
    
    // Group all devices that name contain the word "Macbook" and "iPhone"
    if device.name.contains("Macbook") || device.name.contains("iPhone") {
        return "Apple"
    } else {
        return "Others"
    }
}

/*
// Output of groupByCategoryWithApple equivalent to:
[
    "Apple": [
        Device(category: "Laptop", name: "Macbook Air"),
        Device(category: "Laptop", name: "Macbook Pro"),
        Device(category: "Mobile Phone", name: "iPhone SE"),
        Device(category: "Mobile Phone", name: "iPhone 11"),
    ],
    "Others": [
        Device(category: "Laptop", name: "Galaxy Book"),
        Device(category: "Laptop", name: "Chromebook"),
        Device(category: "Mobile Phone", name: "Galaxy S"),
        Device(category: "Mobile Phone", name: "Galaxy Note"),
        Device(category: "Mobile Phone", name: "Pixel")
    ]
]
*/

按自定义对象分组

在本节中,我们将研究如何使用自定义对象作为init(grouping:by:)初始化器的分组键。

出于演示目的,让我们通过定义Company公司结构来更新前面的示例,并将company变量添加到设备结构中。随后,我们将尝试按公司对所有设备进行分组。

swift 复制代码
struct Company {
    let name: String
    let founder: String
}

struct Device {
    let category: String
    let name: String
    let company: Company
}

// Define Company objects
let samsung = Company(name: "Samsung", founder: "Lee Byung-chul")
let apple = Company(name: "Apple", founder: "Steve Jobs")
let google = Company(name: "Google", founder: "Larry Page")

let deviceArray = [
    Device(category: "Laptop", name: "Macbook Air", company: apple),
    Device(category: "Laptop", name: "Macbook Pro", company: apple),
    Device(category: "Laptop", name: "Galaxy Book", company: samsung),
    Device(category: "Laptop", name: "Chromebook", company: google),
    Device(category: "Mobile Phone", name: "iPhone SE", company: apple),
    Device(category: "Mobile Phone", name: "iPhone 11", company: apple),
    Device(category: "Mobile Phone", name: "Galaxy S", company: samsung),
    Device(category: "Mobile Phone", name: "Galaxy Note", company: samsung),
    Device(category: "Mobile Phone", name: "Pixel", company: google)
]

接下来,让我们检查Dictionary结构的定义,找出成为字典键需要满足的要求。

swift 复制代码
public struct Dictionary<Key, Value> where Key : Hashable

从上述定义中可以看出,任何符合Hashable协议的对象都可以用作字典键。

专业提示:

查看这篇很棒的文章,了解有关Hashable协议的更多信息。

因此,我们可以继续使Company结构符合Hashable协议,并利用init(grouping:by:)初始化器按公司对所有设备进行分组。

swift 复制代码
// Conform to Hashable protocol
struct Company: Hashable {
    let name: String
    let founder: String
}

// ...
// ...

// Use Company object as grouping key
let groupByCompany = Dictionary(grouping: deviceArray) { $0.company }

就这些,我们已经成功地按公司对所有设备对象进行了分组。

正如Reddit用户svetlyo所建议的那样,我们可以通过使用键路径语法使代码更加简洁。

swift 复制代码
let groupByCategory = Dictionary(grouping: deviceArray, by: \.category)

小结

字典的 Init(grouping:by:)初始化器非常有用,并且非常易于使用。

当我想把数组数据分组到字典中,以便在具有多个节的表格视图或集合视图上显示它们时,我发现它特别方便。

下次您想创建包含多个节(Section)的表格视图时,请务必尝试此方法。

我希望这篇文章能让您清楚地了解如何正确使用Dictionary.init(grouping:by:)初始化器。

如果您喜欢这篇文章,请随时查看我的其他 与Swift相关的文章

如果您有任何问题,请随时在下面的评论部分留下,或者您可以在 推特上联系我。

感谢您的阅读。🧑🏻‍💻

相关推荐
Swift社区6 小时前
Apple 新品发布会亮点有哪些 | Swift 周报 issue 61
ios·swiftui·swift
营赢盈英3 天前
OpenAI GPT-3 API error: “You must provide a model parameter“
chatgpt·gpt-3·openai·swift
一只不会编程的猫3 天前
高德地图绘图,点标记,并计算中心点
开发语言·ios·swift
loongloongz3 天前
Swift语言基础教程、Swift练手小项目、Swift知识点实例化学习
开发语言·学习·swift
2401_858120537 天前
深入理解 Swift 中的隐式解包可选类型(Implicitly Unwrapped Optionals)
开发语言·ios·swift
quaer7 天前
QT chart案例
开发语言·qt·swift
安和昂8 天前
【iOS】UIViewController的生命周期
ios·xcode·swift
00圈圈8 天前
Swift 创建扩展(Extension)
ios·swift·extension
2401_858120538 天前
Swift 中的函数:定义、使用与实践指南
开发语言·ssh·swift
quaer9 天前
VS+QT--实现二进制和十进制的转换(含分数部分)
java·开发语言·python·qt·swift