【问题标题】:Merge multiple dictionaries to obtain an array of custom objects合并多个字典,获取自定义对象数组
【发布时间】:2022-01-12 08:52:51
【问题描述】:

我有以下 5 个字典

var serialsDict = [ Source(address: 0, endpoint: 3) : "860408ff",
             Source(address: 0, endpoint: 4) : "560410ff"]
var namesDict = [ Source(address: 0, endpoint: 3) : "SLIGHTR10",
             Source(address: 0, endpoint: 4) : "SLIGHTR09"]
var fwVersionsDict = [ Source(address: 0, endpoint: 3) : "FG01b",
             Source(address: 0, endpoint: 4) : "FG02c"]
var hwVersionDict = [ Source(address: 0, endpoint: 3) : "604a1r00",
             Source(address: 0, endpoint: 4) : "674b1r45"]
var typesDict = [ Source(address: 0, endpoint: 3) : 4,
             Source(address: 0, endpoint: 4) : 1]

Source 在哪里

struct Source: Hashable {
    let address: Int
    let endpoint: Int
}

我必须合并 5 个字典以获得包含 2 个 FoundDevice 对象的数组。其中FoundDevice 是以下结构:

struct FoundDevice {
    
    let source: Source
    
    let serial: String
    let name: String
    let fwVersion: String
    let hwVersion: String
    let type: Int
}

例如第一个元素必须是

FoundDevice(source: Source(address: 0, endpoint: 3), serial: “860408ff”,名称:“SLIGHTR10”,fwVersion:“FG01b”,hwVersion: “604a1r00”,类型:4)

这是我当前的实现:

serialsDict.keys.forEach { source in
    if let serial = serialsDict[source], let name = namesDict[source], let fwVersion = fwVersionsDict[source], let hwVersion = hwVersionDict[source], let type = typesDict[source] {
        devices.append(FoundDevice(source: source, serial: serial, name: name, fwVersion: fwVersion, hwVersion: hwVersion, type: type))
    }
}

有没有更聪明的方法来实现我的目标?

【问题讨论】:

    标签: swift dictionary


    【解决方案1】:

    您需要将一组数据映射到另一组类型。那将是地图。但是由于您使用字典,因此我们需要这里的紧凑地图。

    let sources = serialsDict.map({ $0.key })
    
    let foundDevices: [FoundDevice] = sources.compactMap({
        if let serial = serialsDict[$0],
           let name = namesDict[$0],
           let fwVersion = fwVersionsDict[$0],
           let hwVersion = hwVersionDict[$0],
           let typ = typesDict[$0] {
            return FoundDevice(source: $0, serial: serial, name: name, fwVersion: fwVersion, hwVersion: hwVersion, type: typ)
        }
        return nil 
    })
    

    【讨论】:

    • 我不必使用字典。但我不明白有什么替代方法。
    • 查询一个键返回一个可选的,因为键可能不存在。还不错,但需要处理 Optional
    • if(或者如果你想看的话)确保你拥有所有的值。没有更好的解决方案,因为每个字典可以有不同数量的条目
    • 非常感谢@Helge。我选择@Jessy 的解决方案只是为了避免再次检查其中一个字典中的值,因为我喜欢init 扩展名中的init 的想法。但你的解决方案也是有效的。
    • @HelgeBecker 你不需要map 来从serialsDict 获取密钥,只需使用keys 属性和compactMap 就可以了:let foundDevices = serialsDict.keys.compactMap {…} 也可以对于 compactMap 闭包中的每个键,安全地强制从 serialDict 解包值。
    【解决方案2】:

    没有可用的“技巧”,但您无需再次检查其中一个字典中的值;你已经有了。

    extension FoundDevice {
      init?(
        source: Source,
        serial: String,
        names: [Source: String],
        fwVersions: [Source: String],
        hwVersions: [Source: String],
        types: [Source: Int]
      ) {
        guard
          let name = names[source],
          let fwVersion = fwVersions[source],
          let hwVersion = hwVersions[source],
          let type = types[source]
        else { return nil }
        
        self.init(
          source: source,
          serial: serial,
          name: name,
          fwVersion: fwVersion,
          hwVersion: hwVersion,
          type: type
        )
      }
    }
    
    serialsDict.compactMap {
      FoundDevice(
        source: $0.key,
        serial: $0.value,
        names: namesDict,
        fwVersions: fwVersionsDict,
        hwVersions: hwVersionDict,
        types: typesDict
      )
    }
    

    在下面解决您的评论,

    我的字典可能包含比其他字典更多的键。我必须创造 仅当键在每个字典中时,我的对象。

    无论您使用哪个字典进行迭代,上面的 guard 语句都会处理这个问题。

    但是如果你周围有钥匙的交叉点,不管出于什么原因,那是……

    let sources = [serialsDict, namesDict, fwVersionsDict, hwVersionDict]
      .reduce(into: Set(typesDict.keys)) {
        $0.formIntersection($1.keys)
      }
    

    ...那么您可以安全地强制展开。

    extension FoundDevice {
      /// - Precondition: `source` is a key in all of the dictionaries.
      init(
        source: Source,
        serials: [Source: String],
        names: [Source: String],
        fwVersions: [Source: String],
        hwVersions: [Source: String],
        types: [Source: Int]
      ) {
        self.init(
          source: source,
          serial: serials[source]!,
          name: names[source]!,
          fwVersion: fwVersions[source]!,
          hwVersion: hwVersions[source]!,
          type: types[source]!
        )
      }
    }
    
    sources.map {
      FoundDevice(
        source: $0,
        serials: serialsDict,
        names: namesDict,
        fwVersions: fwVersionsDict,
        hwVersions: hwVersionDict,
        types: typesDict
      )
    }
    

    【讨论】:

    • 好的!抱歉...我的字典可能包含比其他字典更多的键。只有当密钥在每个字典中时,我才必须创建我的对象。
    • 我已经在分隔线下方解决了这个问题。
    • 谢谢@Jessy。我认为以前的实现更安全。
    猜你喜欢
    • 2011-06-24
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 2014-03-05
    相关资源
    最近更新 更多