I come from the .NET world where I had LINQ so I could do in-memory queries like the one we usually see in SQL.
I have a slice of this structure I want to group by 8 fields, and then sum another integer field. Something like:
type Register struct {
id1 int
id2 int
id3 int
id4 int
id5 int
id6 int
id7 int
id8 int
money int
}
I thought in:
Is there a better way or any beautiful, efficient and easy ready to use library?
Basically your idXX
fields are the keys, an n-tuple. And the money
field is the data to be summed.
This can easily be done if you slightly refactor your types. Put only the keys into a struct, so it can be used as a key in a map. Struct values are comparable:
Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
So the new types:
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
}
And to group and calculate sum, you can use a map[Key]int
, using Register.key
as the map key to "group" all registers with the same keys (same ids):
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)
Output:
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]
For a nice output:
fmt.Println("Nice output:")
for k, v := range m {
fmt.Printf("%+3v: %d\n", k, v)
}
Output:
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
This is as easy and as efficient as it can get. Try the examples on the Go Playground.
Notes:
In the map we didn't have to check if a Key
is already in it. This is so because indexing a map yields the zero value of the value type of the map if the key is not in the map. So in this case if a Key
is not yet in the map, m[key]
will give you 0
(0
is the zero value for the int
type), properly telling that the "previous" sum for that key is 0
so far.
Also note that Key
may be an embedded field in Register
instead of a "regular" field, it doesn't matter, and that way you could refer to the idXX
fields as if they were part of the Register
.
You can try go-linq: https://github.com/ahmetalpbalkan/go-linq/
It is similar to c# linq, use the GroupBy and Aggregate in your case
https://godoc.org/github.com/ahmetalpbalkan/go-linq#example-Query-GroupBy https://godoc.org/github.com/ahmetalpbalkan/go-linq#example-Query-Aggregate
pseudo code:
From(regs).GroupBy(merge ids to a string as group key).Select(use Aggregate or SumInts to sum money)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With