... this code just smells very bad. But, cannot figure out better way
to do this.
Let's look at a more complete, more fragrant implementation.
For data, you are using PostgreSQL: db.Query(selectFlightsSQL, pq.Array(codes), ...). Therefore, define data using the SQL Data Definition Language (DDL). For example,
CREATE TABLE airlines (
airlinecode char(3) NOT NULL,
airlinename varchar(64) NOT NULL,
PRIMARY KEY (airlinecode),
UNIQUE (airlinename)
);
CREATE TABLE airports (
airportcode char(3) NOT NULL,
airportname varchar(64) NOT NULL,
timezonename varchar(64) NOT NULL,
PRIMARY KEY (airportcode),
UNIQUE (airportname)
);
CREATE TABLE flights (
airlinecode char(3) NOT NULL,
flightno integer NOT NULL,
departtime timestamptz NOT NULL, -- local time
origincode char(3) NOT NULL,
arrivetime timestamptz NOT NULL, -- local time
destinationcode char(3) NOT NULL,
PRIMARY KEY (airlinecode, flightno, departtime),
FOREIGN KEY (airlinecode) REFERENCES airlines,
FOREIGN KEY (origincode) REFERENCES airports,
FOREIGN KEY (destinationcode) REFERENCES airports
);
Access the data using the SQL Data Manipulation Language (DML). For example,
-- $1 origincode list
-- $2 origincode list length
-- $3 earliest arrivaltime
-- $4 latest arrivaltime
SELECT destinationcode, airportd.airportname AS destinationname, arrivetime, airportd.timezonename AS arrivezone,
origincode, airporto.airportname AS originname, departtime, airporto.timezonename AS departzone,
destinations.airlinecode, airlines.airlinename, flightno
FROM (
SELECT destinationcode, origincode, airlinecode, flightno, departtime, arrivetime,
count(origincode) OVER (PARTITION BY destinationcode) AS origins
FROM flights
WHERE arrivetime >= $3 AND arrivetime <= $4
AND origincode = ANY($1)
AND destinationcode != ANY($1)
) AS destinations
LEFT JOIN airlines ON airlines.airlinecode = destinations.airlinecode
LEFT JOIN airports AS airportd ON airportd.airportcode = destinationcode
LEFT JOIN airports AS airporto ON airporto.airportcode = origincode
WHERE origins = $2
ORDER BY destinationcode, arrivetime, airlinecode, flightno, departtime
;
Consider optimizations such as indices.
Data access is implemented in SQL for any programming language that implements an interface to PostgreSQL (and other relational DBMS`s), not just Go.
In Go, the remaining algorithmic, procedural task is simple: convert the SQL rows to a Go hash map indexed by destination code. For example,
package main
import (
"database/sql"
"time"
"github.com/lib/pq"
_ "github.com/lib/pq"
)
type Flight struct {
DestinationCode string
DestinationName string
ArriveTime time.Time
ArriveZone string
OriginCode string
OriginName string
DepartTime time.Time
DepartZone string
AirlineCode string
AirlineName string
FlightNo int
}
func destinations(db *sql.DB, origins []string, earliest, latest time.Time) (map[string][]Flight, error) {
query := `
-- $1 origincode list
-- $2 origincode list length
-- $3 earliest arrivaltime
-- $4 latest arrivaltime
SELECT destinationcode, airportd.airportname AS destinationname, arrivetime, airportd.timezonename AS arrivezone,
origincode, airporto.airportname AS originname, departtime, airporto.timezonename AS departzone,
destinations.airlinecode, airlines.airlinename, flightno
FROM (
SELECT destinationcode, origincode, airlinecode, flightno, departtime, arrivetime,
count(origincode) OVER (PARTITION BY destinationcode) AS origins
FROM flights
WHERE arrivetime >= $3 AND arrivetime <= $4
AND origincode = ANY($1)
AND destinationcode != ANY($1)
) AS destinations
LEFT JOIN airlines ON airlines.airlinecode = destinations.airlinecode
LEFT JOIN airports AS airportd ON airportd.airportcode = destinationcode
LEFT JOIN airports AS airporto ON airporto.airportcode = origincode
WHERE origins = $2
ORDER BY destinationcode, arrivetime, airlinecode, flightno, departtime
;
`
dst := make(map[string][]Flight)
rows, err := db.Query(query, pq.Array(origins), len(origins), earliest, latest)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var flt Flight
err := rows.Scan(
&flt.DestinationCode,
&flt.DestinationName,
&flt.ArriveTime,
&flt.ArriveZone,
&flt.OriginCode,
&flt.OriginName,
&flt.DepartTime,
&flt.DepartZone,
&flt.AirlineCode,
&flt.AirlineName,
&flt.FlightNo,
)
if err != nil {
return nil, err
}
dst[flt.DestinationCode] = append(dst[flt.DestinationCode], flt)
}
err = rows.Err()
if err != nil {
return nil, err
}
return dst, nil
}
func main() {}
The XY problem is asking about your attempted solution rather than your actual problem: The XY Problem. Therefore, without a problem, we have no way of knowing whether a solution is correct. There are no use cases or examples.
TODO: Domain knowledge is important. Airline arrival and departure times are local times. Any solution needs to be verified and tested across time zones and for standard and daylight saving times.