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