Golang:对结构进行分组和求和

Golang: group and sum slice of structs

我来自 .NET 世界,在那里我有 LINQ,所以我可以像我们通常在 SQL 中看到的那样进行内存中查询。

我有这个结构的一部分,我想按 8 个字段分组,然后对另一个整数字段求和。类似于:

type Register struct {
    id1 int
    id2 int
    id3 int
    id4 int
    id5 int
    id6 int
    id7 int
    id8 int
    money int
}

我认为:

有没有更好的方法或者有什么漂亮高效易用的方法 图书馆?

基本上你的 idXX 字段是键,一个 n 元组。而money字段就是要求和的数据。

如果您稍微重构您的类型,这可以很容易地完成。仅将键放入结构中,因此它可以用作映射中的键。结构值为 comparable:

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

所以新类型:

type Key struct {
    id1 int
    id2 int
    id3 int
    id4 int
    id5 int
    id6 int
    id7 int
    id8 int
}

type Register struct {
    key   Key
    money int
}

为了分组和计算总和,您可以使用 map[Key]int,使用 Register.key 作为映射键到 "group" 所有具有相同键(相同 ID)的寄存器:

regs := []*Register{
    {Key{id1: 345}, 1500},
    {Key{id1: 345, id2: 140}, 2700},
    {Key{id1: 345, id2: 140}, 1300},
    {Key{id1: 345}, 1000},
    {Key{id3: 999}, 1000},
    {Key{id3: 999}, 2000},
}

// calculate sum:
m := map[Key]int{}
for _, v := range regs {
    m[v.key] += v.money
}

fmt.Println(m)

输出:

map[{345 0 0 0 0 0 0 0}:2500 {345 140 0 0 0 0 0 0}:4000 {0 0 999 0 0 0 0 0}:3000]

为了一个好的输出:

fmt.Println("Nice output:")
for k, v := range m {
    fmt.Printf("%+3v: %d\n", k, v)
}

输出:

Nice output:
{id1:345 id2:  0 id3:  0 id4:  0 id5:  0 id6:  0 id7:  0 id8:  0}: 2500
{id1:345 id2:140 id3:  0 id4:  0 id5:  0 id6:  0 id7:  0 id8:  0}: 4000
{id1:  0 id2:  0 id3:999 id4:  0 id5:  0 id6:  0 id7:  0 id8:  0}: 3000

这是尽可能简单和高效的。尝试 Go Playground.

上的示例

备注:

在地图中,我们不必检查 Key 是否已经在其中。之所以如此,是因为如果键不在地图中,索引地图会产生地图值类型的 zero value。因此,在这种情况下,如果 Key 尚未在地图中,则 m[key] 将为您提供 00int 类型的零值) ,正确地说明该键的 "previous" 总和到目前为止是 0

另请注意,Key 可能是 Register 中的 embedded field 而不是 "regular" 字段,没关系,这样你可以参考idXX 字段就好像它们是 Register.

的一部分一样

你可以试试go-linq:https://github.com/ahmetalpbalkan/go-linq/

它类似于 c# linq,在您的情况下使用 GroupBy 和 Aggregate

https://godoc.org/github.com/ahmetalpbalkan/go-linq#example-Query-GroupBy https://godoc.org/github.com/ahmetalpbalkan/go-linq#example-Query-Aggregate

伪代码:

From(regs).GroupBy(merge ids to a string as group key).Select(use Aggregate or SumInts to sum money)