Reputation: 719
I want to combine two arrays thanks to compactMap
. There are two lists - Stations and Sensors. In the Sensor object there is a property named: stationId
. It helps to combine Sensors with Stations, but there is a problem.
Some sensors are owned by the same stationId
. So one Station can own several Sensors. Three, five, or even one and so on. But I can't do it by compactMap
. I always have only one Sensor per Station. How to fix this? How to make more Sensors per Station in my case?
func setAddItemList(stations: [Station], sensors: [Sensor]) {
if stations.count > 0 && sensors.count > 0 {
addItemList = stations.compactMap { station in
guard let sensor = sensors.first(where: { $0.stationId == station.id}) else {
return nil
}
return AddItem(id: station.id, stationId: sensor.stationId, cityName: station.city!.name, addressStreet: station.addressStreet!, sensor: ([SensorItem(id: sensor.id, stationId: sensor.stationId, param: (ParamItem(paramName: sensor.param!.paramName, paramFormula: sensor.param!.paramFormula, paramCode: sensor.param!.paramCode, idParam: sensor.param!.idParam)))]) )
}
if addItemList.count > 0 {
addItem.onNext(addItemList)
}
}
}
Upvotes: 0
Views: 275
Reputation: 63137
One of these lines is 375 lines long, with 2 sets of unnecessary braces. Before I even do anything, just tidying this up makes it much easier to read:
func setAddItemList(stations: [Station], sensors: [Sensor]) {
if sensors.count > 0 {
addItemList = stations.compactMap { station in
guard let sensor = sensors.first(where: { $0.stationId == station.id}) else {
return nil
}
return AddItem(
id: station.id,
stationId: sensor.stationId,
cityName: station.city!.name,
addressStreet: station.addressStreet!,
sensor: [
SensorItem(
id: sensor.id,
stationId: sensor.stationId,
param: ParamItem(
paramName: sensor.param!.paramName,
paramFormula: sensor.param!.paramFormula,
paramCode: sensor.param!.paramCode,
idParam: sensor.param!.idParam
)
),
]
)
}
if addItemList.count > 0 {
addItem.onNext(addItemList)
}
}
}
The check for stations.count > 0
does nothing, so I removed it.
I would replace this entire approach. compactMap
is for mapping and filtering out nil
s. In each invvocation of its closure, you're doing a linear scan through the sensors to find the first one that belongs to the station. That's slow, but as you found, it's also incorrect, because it'll always land on the same item.
Instead, I would use a dictionary to key sensors by their ID. Something like this:
func setAddItemList(stations: [Station], sensors: [Sensor]) {
let sensorsByStationID = Dictionary(grouping: sensors, by: \.stationId)
let sensorItemsByStationID = sensorsByStationID.mapValues { sensor in
return SensorItem(
id: sensor.id,
stationId: sensor.stationId,
param: ParamItem( // FIXME: ParamItem is a terrible name
paramName: sensor.param!.paramName,
paramFormula: sensor.param!.paramFormula,
paramCode: sensor.param!.paramCode,
idParam: sensor.param!.idParam
)
)
}
let addItems = stations.map { station in
AddItem( // TODO: AddItem? What does that have to do with a "Station" or a "Sensor"?
id: station.id,
stationId: sensor.stationId,
cityName: station.city!.name,
addressStreet: station.addressStreet!,
sensor: sensorItemsByStationID[station.id] ?? [] // FIXME: This keyword label should be `sensorItems`, not singular.
)
}
if !addItems.isEmpty {
addItem.onNext(addItems) // FIXME: this might be a misplaced responsibility in this function.
}
}
Upvotes: 1
Reputation: 51821
I would use a forEach
on the sensors array to match each sensor with a station
sensors.forEach { sensor in
guard let index = stations.firstIndex(where: { $0.id == sensor.stationId }) else {
print("Failed to find a station for sensor \(sensor.id)")
return
}
stations[index].sensors.append(sensor)
}
Upvotes: 2