Swift 一维字典转二维

Swift 1D Dictionary to 2D

我对二维字典的理解有点问题。我的函数必须 return 带有部分的 UITableView 字典。 1 个模板类型可以有多个模板字符串。因此,当 fetchedData 中有 2 个或更多具有相似类型的文本时,它们必须位于具有 1 个键的数组 [String] 中 - String。 从编译器的角度来看,下面的代码是绝对正确的。至于我 smth 是错误的,但漂亮的自动完成让我觉得一切都很好。 显然它 return 是一个空字典 [:]

func fetchTemplates() -> Dictionary<String, [String]> {
    var templates: Dictionary<String, [String]> = [:]
    let fetchRequest: NSFetchRequest<Template> = Template.fetchRequest()
    fetchRequest.sortDescriptors = [SortDescriptor.init(key: "templateType", ascending: true)]
    let fetchedData = try! context.fetch(fetchRequest)
    if (!fetchedData.isEmpty) {
        for templateItem in fetchedData {
            templates[templateItem.templateType!]?.append(templateItem.templateText!)
        }
        return templates
    }
    else {
        return templates
    }
}

P.S。 fetchedData returns:

<Template: 0x003281h4> (entity: Template; id: 0x003281h4 <x-coredata:///Template/> ; data: {
    templateText = "Example";
    templateType = "First";
})

问题是:

templates[templateItem.templateType!] 总是 nil 因为字典是空的。

因此无法附加任何内容。

问题出在这一行:

templates[templateItem.templateType!]?.append(templateItem.templateText!)

templates 使用此行初始化:var templates: Dictionary<String, [String]> = [:]。此时,templates是一个空字典。

让我们按时间顺序将该行分解为发生的步骤:

  1. templateItem.templateType 被访问,并强制展开。如果是 nil.
  2. 会发生崩溃
  3. templateItem.templateType! 用作 templates 字典的键。这将始终 return nil。字典是空的,因此没有任何键的值,包括这个键。
  4. ?.append() 被调用,条件是它没有在 nil 上被调用。如果在 nil 上调用它,则不会发生任何事情。

3 是导致您出现问题的原因。如果键还不存在,则需要初始化一个新数组:

func fetchTemplates() -> Dictionary<String, [String]> {
    var templates: Dictionary<String, [String]> = [:]
    let fetchRequest: NSFetchRequest<Template> = Template.fetchRequest()
    fetchRequest.sortDescriptors = [SortDescriptor.init(key: "templateType", ascending: true)]
    let fetchedData = try! context.fetch(fetchRequest)
    if (!fetchedData.isEmpty) { //see note 2
        for templateItem in fetchedData {
           let type = templateItem.templateType!
           var array = templates[type] ?? [] //see note 1
           array!.append(templateItem.templateText!)
           templates[type] = array
        }
        return templates
    }
    else {
        return templates
    }
}

这个函数可以简化:

func fetchTemplates() -> [String : [String]] {
    let fetchRequest = Template.fetchRequest()
    fetchRequest.sortDescriptors = [SortDescriptor(key: "templateType", ascending: true)]

    let fetchedData = try! context.fetch(fetchRequest)

    var templates = [String, [String]]()
    for templateItem in fetchedData {
        let type = templateItem.templateType!
        templates[type] = (templates[text] ?? []) + [templateItem.templateText!]
    }
    return templates
}

可以改用reduce:

func fetchTemplates() -> [String : [String]] { //see note 3
    let fetchRequest = Template.fetchRequest() //see note 4
    fetchRequest.sortDescriptors = [SortDescriptor(key: "templateType", ascending: true)] //see note 5

    let fetchedData = try! context.fetch(fetchRequest)

    return fetchedData.reduce([String, [String]]()){templates, templateItem in
        (templates[templateItem.tempalteText!] ?? []) + [templateItem.templateText!]
    } //see note 6
}

备注

  1. 如果 template[text] 不是 nil,则分配给 array。否则,一个新数组 ([]) 被分配给 `array.
  2. 这个检查是不必要的
  3. Dictionary<String, [String]> 可以写成 [String : [String]]
  4. 不需要明确的类型签名
  5. X.init() 可以写成 X()
  6. 不需要空值检查,整个for循环可以改为reduce调用。