【问题标题】:Golang: group and sum slice of structsGolang:对结构进行分组和求和
【发布时间】: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 的项目都在不同的组等中。你能举一个输入和输出的例子吗?不需要大量数据,但足以说明您的结构不需要的点。
  • 可以,例如:
  • 是的,例如:[r1{id1: 345, money: 1500}, r2{id1: 345, id2 140, money: 2700}, r3{id1: 345, money: 1000}。所以 r1 和 r3 应该相加得到rSummed1{id: 345, money: 2500}, rSummed2{id1: 345, id2: 140, money: 2700}。是经典的SELECT Sum(money) GROUP BY id1, id2 ...
  • r2 不会也在那个组吗? id1 的值也是 345。
  • 否,因为在 r2 中 id2 != 0。所有 id 字段必须完全相同。

标签: dictionary go struct grouping slice


【解决方案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{
    {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 类型的零值),正确地告诉你“以前”的总和到目前为止,该密钥是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)

【讨论】:

  • 太棒了!似乎正是我需要的!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-25
  • 2015-05-17
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
相关资源
最近更新 更多