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

Posted

技术标签:

【中文标题】Golang:对结构进行分组和求和【英文标题】:Golang: group and sum slice of structs 【发布时间】:2017-02-20 13:43:45 【问题描述】:

我来自 .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

我想到了:

创建一个 Equal 函数来比较结构(这八个 字段)。 遍历我正在分析的集合。对于每个项目 检查它是否已经在哈希表中。如果有 => 我总结 场。如果不是 => 我将新项目添加到哈希表中。

有没有更好的方法或任何美观、高效且易于使用的方法 图书馆?

【问题讨论】:

我并没有真正理解你的问题。 Equal 函数的目的是什么?我不确定您是否期待地图的地图,其中所有具有 id1 值 X 的项目都在一个组中,所有具有另一个 id1 值 Y 的项目都在不同的组等中。你能举一个输入和输出的例子吗?不需要大量数据,但足以说明您的结构不需要的点。 可以,例如: 是的,例如:[r1id1: 345, money: 1500, r2id1: 345, id2 140, money: 2700, r3id1: 345, money: 1000。所以 r1 和 r3 应该相加得到rSummed1id: 345, money: 2500, rSummed2id1: 345, id2: 140, money: 2700。是经典的SELECT Sum(money) GROUP BY id1, id2 ... r2 不会也在那个组吗? id1 的值也是 345。 否,因为在 r2 中 id2 != 0。所有 id 字段必须完全相同。 【参考方案1】:

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

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

如果结构值的所有字段都是可比较的,则结构值是可比较的。如果对应的非blank 字段相等,则两个结构值相等。

所以新类型:

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 作为映射键来“分组”具有相同键(相同ID)的所有寄存器:

regs := []*Register
    Keyid1: 345, 1500,
    Keyid1: 345, id2: 140, 2700,
    Keyid1: 345, id2: 140, 1300,
    Keyid1: 345, 1000,
    Keyid3: 999, 1000,
    Keyid3: 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 类型的零值),正确地告诉你“以前”的总和到目前为止,该密钥是0

另请注意,Key 可能是 Register 中的 embedded field 而不是“常规”字段,这无关紧要,这样您就可以引用 idXX 字段,就好像它们是一部分一样Register.

【讨论】:

太棒了,这正是我一直在寻找的东西。我知道有更好的方法!谢谢!!【参考方案2】:

你可以试试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)

【讨论】:

太棒了!似乎正是我需要的!谢谢!

以上是关于Golang:对结构进行分组和求和的主要内容,如果未能解决你的问题,请参考以下文章

golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和

golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和

golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和

golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和

[golang] 数据结构-希尔排序

Golang入门到项目实战 golang结构体指针