In golang application I make query to such table:
| ID | AGG_YEAR | AGG_MONTH | GENDER | AGE_RANGE | INCOME_RANGE | TOTAL |
|-----------|----------|-----------|--------|-----------|--------------|-------|
| 107502389 | 2019 | 7 | F | 18_29 | 1000_2000 | 15 |
| 107502389 | 2019 | 7 | F | 18_29 | 2000_4000 | 42 |
| 107502389 | 2019 | 7 | F | 30_44 | 1000_2000 | 25 |
| 107502389 | 2019 | 7 | F | 30_44 | 2000_4000 | 63 |
| 107502389 | 2019 | 7 | M | 18_29 | 1000_2000 | 30 |
| 107502389 | 2019 | 7 | M | 18_29 | 2000_4000 | 18 |
| 107502389 | 2019 | 7 | M | 30_44 | 1000_2000 | 36 |
| 107502389 | 2019 | 7 | M | 30_44 | 2000_4000 | 19 |
This table stores information about the total number of males and females with a certain wage level in a certain month. Usually, after a query to the database, each record is parsed separately one by one:
type Entry struct {
ID int `json:"id"`
AggYear int `json:"agg_year"`
AggMonth int `json:"agg_month"`
Gender string `json:"gender"`
AgeRange string `json:"age_range"`
IncomeRange string `json:"income_range"`
Total int `json:"total"`
}
var entries []Entry
rows, err := database.Query("***"); if err != nil {
fmt.Println(err)
return
}
defer rows.Close()
for rows.Next() {
var entry Entry
if err = rows.Scan(&entry.ID, &entry.AggMethod, &entry.AggYear, &entry.AggMonth, &entry.Gender, &entry.AgeRange, &entry.IncomeRange, &entry.Total); err != nil {
fmt.Println(err)
return
}
entries = append(entries, entry)
}
type IncomeDetails struct {
IncomeRange string `json:"income_range"`
Total int `json:"total"`
}
type AgeDetails struct {
AgeRange string `json:"age_range"`
Details []IncomeDetails `json:"details"`
}
type GenderDetails struct {
Gender string `json:"gender"`
Details []AgeDetails `json:"details"`
}
type EntryDetails struct {
AggYear int `json:"agg_year"`
AggMonth int `json:"agg_month"`
Details []GenderDetails `json:"details"`
}
type DataEntry struct {
ID int `json:"id"`
Details []EntryDetails `json:"details"`
}
var entryDetails []EntryDetails
i:= 0
for i < len(entries) {
var genderDetails []GenderDetails
aggYear := entries[i].AggYear
aggMonth := entries[i].AggMonth
for aggYear == entries[i].AggYear && aggMonth == entries[i].AggMonth {
gender := entries[i].Gender
var ageDetails []AgeDetails
for gender == entries[i].Gender {
ageRange := entries[i].AgeRange
var incomeDetails []IncomeDetails
for ageRange == entries[i].AgeRange && gender == entries[i].Gender { // <- runtime error: index out of range
incomeDetail := IncomeDetails{entries[i].IncomeRange, entries[i].Total}
incomeDetails = append(incomeDetails, incomeDetail)
i++
}
ageDetail := AgeDetails{entries[i-1].AgeRange, incomeDetails}
ageDetails = append(ageDetails, ageDetail)
i++
}
genderDetail := GenderDetails{entries[i-1].Gender, ageDetails}
genderDetails = append(genderDetails, genderDetail)
i++
}
entryDetail := EntryDetails{entries[i-1].AggYear, entries[i-1].AggMonth, genderDetails}
entryDetails = append(entryDetails, entryDetail)
i++
}
I want to know the best way to group the following values as in the example below? I want to understand the sequence of actions. I will be grateful for any help.
[
{
"id": 107502389,
"details": [
{
"agg_year": 2019,
"agg_month": 7,
"details": [
{
"gender": "F",
"details": [
{
"age_range": "18_29",
"details": [
{
"income_range": "1000_2000",
"total": "15"
},
{
"income_range": "2000_4000",
"total": "42"
},
]
},
{
"age_range": "30_44",
"details": [
{
"income_range": "1000_2000",
"total": "25"
},
{
"income_range": "2000_4000",
"total": "63"
},
]
}
]
},
{
"gender": "M",
"details": [
{
"age_range": "18_29",
"details": [
{
"income_range": "1000_2000",
"total": "30"
},
{
"income_range": "2000_4000",
"total": "18"
},
]
},
{
"age_range": "30_44",
"details": [
{
"income_range": "1000_2000",
"total": "36"
},
{
"income_range": "2000_4000",
"total": "19"
},
]
}
]
}
]
}
]
}
]
Usually, I would be updating the Structure according to the response i want. For example in your case it would be :
type DataEntry struct {
ID int `json:"id"`
Details []EntryDetails `json:"details"`
}
type EntryDetails struct {
AggYear int `json:"agg_year"`
AggMonth int `json:"agg_month"`
Details []GenderDetails `json:"details"`
}
type GenderDetails struct {
Gender string `json:"gender"`
Details []AgeDetails `json:"details"`
}
type AgeDetails struct {
AgeRange string `json:"age_range"`
Details []IncomeDetails `json:"details"`
}
type IncomeDetails struct {
IncomeRange string `json:"income_range"`
Total int `json:"total"`
}
Dividing your code into smaller parts is always easier to read and maintain.
Next part to add the details to the struct : you should Query your code according to the requirements to fill the struct one by one. For example: getID-'Entry struct' first, then getAggYear & getAggMonth for the ID-'EntryDetails struct' and so one.
You can find the full working program here : https://play.golang.org/p/_pdb5y9Wd-O
Enjoy !