4

Let's say I've got the following structs:

type A struct { Field1 string Field2 string } type B struct { Field3 string Field4 int } 

and one more struct which uses the previous two:

type C struct { A MyList []B } 

Now, I've got some data that I want to group and map into a C struct and return a slice of C:

var results []C 

I've got a slice of structs that looks like (since it's a slice if could happen that we have repeated A):

type X struct { A B } 

So I want to group the results by A. To do so I'm gonna iterate through a slice of X that my method receive as parameter:

var results []C // Y is an slice of Xs for _, elem := range Y { // If elem.A exists then append elem.B into C.A // If not then add elem.A and elem.B into C } 

How can achieve the stuff stated in the comments above? I mean, how could I check if a struct A exists already in the slice of structs C?

3
  • so, what is the type of X ? Commented Jan 2, 2022 at 20:31
  • sorry, I updated the question. Renamed to Y to avoid confusion. Basically Y is an slice of Xs Commented Jan 2, 2022 at 20:33
  • how would you do if those were not structs ? What else would you care about if nott comparability ? Commented Jan 2, 2022 at 20:48

1 Answer 1

3

Usually when you want to aggregate something by some value, you use a map. The key will be the value you want to aggregate by, type of A in your case.

This is easiest here too, this only requires the key type to be comparable.

You may simply gather the data like this:

// Example data: xs := []X{ {A{"a1", "a2"}, B{"b1", 2}}, {A{"a1", "a2"}, B{"b11", 22}}, {A{"a1", "a3"}, B{"b4", 5}}, } m := map[A][]B{} for _, x := range xs { m[x.A] = append(m[x.A], x.B) } fmt.Println(m) 

This will output:

map[{a1 a2}:[{b1 2} {b11 22}] {a1 a3}:[{b4 5}]] 

If you do need the result as []C, iterate over the map and populate it:

for a, bs := range m { results = append(results, C{a, bs}) } fmt.Println(results) 

This will output:

[{{a1 a2} [{b1 2} {b11 22}]} {{a1 a3} [{b4 5}]}] 

Try the examples on the Go Playground.

If by any chance A wouldn't be comparable, you would have to resort to using loops instead of map lookups where you would have to manually check for equivalence, and if you find a match, you'd perform the append operation.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks icza. I had initially tried something similar but it was giving me an error. After looking at your example I know where the issue was: I was declaring var m map[A][]B and error it was throwing reads: assignment to entry in nil map. I changed the declaration to the one you have suggested and it's working although I'm still not clear on why it didn't work with the declaration without the initialization. Thanks
If you use var m map[A][]B, then the m map will remain its zero value (which is nil for maps). You can't put entries into a nil map. The short variable declaration I used: m := map[A][]B{}, it uses a composite literal to create a non-nil map value, which is assigned to m. This is an initialized map in which you may put entries. You could also write it like: var m = map[A][]B{}, or do the assignment after a variable declaration: var m map[A][]B; m = map[A][]B{}. Or use make() like this: m = make(map[A][]B)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.