Home > Blockchain >  Merge multiple dictionaries to obtain an array of custom objects
Merge multiple dictionaries to obtain an array of custom objects

Time:12-07

I have the following 5 dictionaries

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]

where Source is

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

I have to merge the 5 dictionaries to obtain an array with 2 FoundDevice objects. Where FoundDevice is the following struct:

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

For example the first element has to be

FoundDevice(source: Source(address: 0, endpoint: 3), serial: "860408ff", name: "SLIGHTR10", fwVersion: "FG01b", hwVersion: "604a1r00", type: 4)

This is my current implementation:

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))
    }
}

Is there a smarter way to achieve my goal?

CodePudding user response:

There's no "trick" available, but you don't need to check for a value in one of the dictionaries again; you already have that.

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
  )
}

Addressing your comment below,

my dictionaries could contain more keys than other. I have to create my object only if the key is in every dictionary.

The guard statement above handles that, no matter which of the dictionaries you use for iteration.

But if you have the intersection of the keys around, for whatever reason, which is…

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

…then you can safely force unwrap.

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
  )
}

CodePudding user response:

You need to map one set of data to another set of a type. That would be map. But since you use dictionarys, we need compact map here.

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 
})
  • Related