如何正确分组数据以获得树结构?

How to group data correctly to get a tree structure?

在 golang 应用程序中,我查询了这样的 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    |

这个table存储了某个月某个工资水平的男性和女性总数的信息。通常,查询数据库后,每条记录都会被一条一条地解析:

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

我想知道将以下值分组的最佳方法,如下例所示?我想了解操作的顺序。我将不胜感激。

[
    {
        "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"
                                    },
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

通常,我会根据我想要的响应更新结构。例如,在您的情况下,它将是:

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"`
}

将您的代码分成更小的部分总是更容易阅读和维护。

下一部分将详细信息添加到结构中:您应该根据要求查询您的代码以逐一填充结构。例如:首先是 getID-'Entry struct',然后是 ID-'EntryDetails struct' 的 getAggYear 和 getAggMonth,依此类推。

您可以在这里找到完整的工作程序:https://play.golang.org/p/_pdb5y9Wd-O

尽情享受吧!