Creating a new slice from an array of objects golang and group - go

I have an array of objects that I would like to use to create a new slice while grouping a field for objects with the same id(the id, in this case, the id is pay_method_id) into an array of objects. i.e I want to group all users with a particular payment method
sample input
[{
"user_id": 1,
"pay_method_id": 1
}
{
"user_id": 2,
"pay_method_id": 2
}
{
"user_id": 4,
"pay_method_id": 1
}
{
"user_id": 3,
"pay_method_id": 2
}]
expected output
[
{"pay_method_id" : "2",
"users": [{"user_id": 2}, {"user_id": 3}]
}
{
"pay_method_id" : "1",
"users": [{"user_id": 4}, {"user_id": 1}]
}
]
Struct representing input
type paymenthodUsers struct{
PayMethodID int
UserID int
}
Struct for the output
type paymentMethodGrouped struct{
PayMethodID int
Users []user
}
type user struct{
UserID int
}
How do I generate the expected output above in golang?

package main
import (
"encoding/json"
"fmt"
"log"
)
type paymenthodUsers struct {
PayMethodID int `json:"pay_method_id"`
UserID int `json:"user_id"`
}
type paymentMethodGrouped struct {
PayMethodID int `json:"pay_method_id"`
Users []user `json:"users"`
}
type user struct {
UserID int `json:"user_id"`
}
func main() {
_json := `[{
"user_id": 1,
"pay_method_id": 1
},
{
"user_id": 2,
"pay_method_id": 2
},
{
"user_id": 4,
"pay_method_id": 1
},
{
"user_id": 3,
"pay_method_id": 2
}]`
var paymentmethods []paymenthodUsers
err := json.Unmarshal([]byte(_json), &paymentmethods)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Original input : %+v\n", paymentmethods)
groups := make(map[int][]user)
for _, pm := range paymentmethods {
if _, found := groups[pm.PayMethodID]; !found {
groups[pm.PayMethodID] = []user{}
}
groups[pm.PayMethodID] = append(groups[pm.PayMethodID], user{pm.UserID})
}
paymentGroups := []paymentMethodGrouped{}
for k, v := range groups {
paymentGroups = append(paymentGroups, paymentMethodGrouped{k, v})
}
data, err := json.Marshal(paymentGroups)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Grouped: %s", string(data))
}
Go Playground Demo

Related

How to flatten a Struct with embedded structs to json

Given the following struct types, StructA and StructB that are embedded in CompleteStruct
type StructA struct {
A int `json:"a_a"`
B int `json:"a_b"`
C int `json:"a_c"`
}
type StructB struct {
A int `json:"b_a"`
B int `json:"b_b"`
}
type CompleteStruct struct {
Name string `json:"name"`
StructA
StructB
}
And s which is a new struct.
s := CompleteStruct{Name: "Example",
StructA: StructA{
A: 1,
B: 2,
C: 3,
},
StructB: StructB{
A: 4,
B: 5,
},
}
How do you transform s to the following json.
[
{
"name": "Example",
"field": "a_a",
"value": 1
},
{
"name": "Example",
"field": "a_b",
"value": 2
},
{
"name": "Example",
"field": "a_c",
"value": 3
},
{
"name": "Example",
"field": "b_a",
"value": 4
},
{
"name": "Example",
"field": "b_b",
"value": 5
}
]
Note: In reality, CompleteStruct will contain 10 or more embedded structs and each embedded struct will contain 10 or more fields. So I would like a solution that does not require typing each field out individually, I assume this will require using reflection
You can't solve it without reflection. Simple example:
func (u *CompleteStruct) MarshalJSON() ([]byte, error) {
type Result struct {
Name string `json:"name"`
Field string `json:"field"`
Value any `json:"value"`
}
var res []Result
val := reflect.ValueOf(u).Elem()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
switch field.Kind() {
case reflect.Struct:
for i := 0; i < field.NumField(); i++ {
tp := field.Type().Field(i)
field := field.Field(i)
res = append(res, Result{
Name: u.Name,
Field: tp.Tag.Get("json"),
Value: field.Interface(),
})
}
}
}
return json.Marshal(res)
}
PLAYGROUND
This should give you the structure you want:
package main
import (
"encoding/json"
"os"
"reflect"
)
type StructA struct {
A int `json:"a_a"`
B int `json:"a_b"`
C int `json:"a_c"`
}
type StructB struct {
A int `json:"b_a"`
B int `json:"b_b"`
}
type CompleteStruct struct {
Name string `json:"name"`
StructA
StructB
}
func main() {
s := CompleteStruct{Name: "Example",
StructA: StructA{
A: 1,
B: 2,
C: 3,
},
StructB: StructB{
A: 4,
B: 5,
},
}
flat(s)
json.NewEncoder(os.Stdout).Encode(results)
}
type resp struct {
Name string `json:"name"`
Field string `json:"field"`
Value any `json:"value"`
}
var globalName string
var results []resp
func flat(s interface{}) {
st := reflect.TypeOf(s)
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
if field.Type.Kind() == reflect.Struct {
flat(reflect.ValueOf(s).Field(i).Interface())
} else {
name := field.Tag.Get("json")
if name == "name" {
globalName = reflect.ValueOf(s).Field(i).String()
continue
}
results = append(results, resp{Name: globalName, Field: name, Value: reflect.ValueOf(s).Field(i).Interface()})
}
}
}
go run ./main.go | jq '.'
Go Playground

Golang: Create nested from linear array bassed on id and parent id

I have a data linear of name such as:
name: One, Id: 1, ParentId: 0
name: One-One, Id: 2, ParentId: 1
name: One-One-One, Id: 3, ParentId: 2
name: One-One-Two, Id: 4, ParentId: 2
For example this data, I get from the database, but I think to test the logic I make the dummy data to struct.
I think I make a temporary index, for data recursively. I set if data does not exist in a map, and I get index if data has to append for before slice. But, I think in function recursive (i show it bellow), it doesn't work (data not append). Why?
is there any wrong algorithm logic?
And what is the right solution for my result is
[
{
"id": 1,
"name": "One",
"children": [
{
"id": 2,
"name": "One-One",
"children": [
{
"id": 3,
"name": "One-One-One",
"children": null
},
{
"id": 4,
"name": "One-One-Two",
"children": null
}
]
}
]
}
]
Full code in golang:
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
}
type Datas []Data
type Response struct {
Id int `json:"id"`
Name string `json:"name"`
Children Responses `json:"children"`
}
type Responses []*Response
func main() {
datas := Datas{
{
Name: "One",
Id: 1,
},
{
Name: "One-One",
Id: 2,
ParentId: 1,
},
{
Name: "One-One-One",
Id: 3,
ParentId: 2,
},
{
Name: "One-One-Two",
Id: 4,
ParentId: 2,
},
}
var result Responses
tempIdx := make(map[int]int)
for _, val := range datas {
res := Response{
Id: val.Id,
Name: val.Name,
}
if val.ParentId == 0 {
result = append(result, &res)
tempIdx[val.Id] = len(result) - 1
continue
} else {
recursive(val.ParentId, result, res, tempIdx)
}
}
json, err := json.Marshal(result)
if err != nil {
panic(err)
}
fmt.Println(string(json))
}
func recursive(idxParent int, datas Responses, res Response, tempIdx map[int]int) {
idxData, ok := tempIdx[idxParent]
if ok {
// don't work in this "datas[idxData].Children", why?
recursive(idxData, datas[idxData].Children, res, tempIdx)
} else {
datas = append(datas, &res)
tempIdx[res.Id] = len(datas) - 1
}
}
Open with Golang Playground
Slice is not an array
appending to slice in function doesn't increase length and capacity of original slice.
change := func(slice []int) {
slice = append(slice, 3)
}
slice := []int{1, 2}
change(slice)
fmt.Println(slice)
// Output: [1 2]
Anyway even if you fix the slice issue your output won't be as expected. You are basically using a Tree data structure so it's recommended to use some of tree searching algorithms. Here's your working example with BFS
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
}
type Datas []Data
type Response struct {
Id int `json:"id"`
Name string `json:"name"`
Children Responses `json:"children"`
}
type Responses []*Response
func main() {
datas := Datas{
{
Name: "One",
Id: 1,
},
{
Name: "One-One",
Id: 2,
ParentId: 1,
},
{
Name: "One-One-One",
Id: 3,
ParentId: 2,
},
{
Name: "One-One-Two",
Id: 4,
ParentId: 2,
},
}
var result Responses
for _, val := range datas {
res := &Response{
Id: val.Id,
Name: val.Name,
}
var found bool
// iterate trough root nodes
for _, root := range result {
parent := findById(root, val.ParentId)
if parent != nil {
parent.Children = append(parent.Children, res)
found = true
break
}
}
if !found {
result = append(result, res)
}
}
out, err := json.Marshal(result)
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
func findById(root *Response, id int) *Response {
queue := make([]*Response, 0)
queue = append(queue, root)
for len(queue) > 0 {
nextUp := queue[0]
queue = queue[1:]
if nextUp.Id == id {
return nextUp
}
if len(nextUp.Children) > 0 {
for _, child := range nextUp.Children {
queue = append(queue, child)
}
}
}
return nil
}

Extract all JSON keys and values as map

i want to get all keys and values of json fields as type map[string][]string. The objects with same keys, should be in same map field.
I can convert JSON to map[string]interface{} now with this function, but it doesn't meet my needs.
func jsonToMap(data []byte) map[string]interface{} {
x := map[string]interface{}
json.Unmarshal(data, &x)
return x
}
Sample json:
{
"data": {
"name": "John",
"age": 30
},
"items": [
1,
2,
3
],
"data2": {
"name": "Johns",
"age": {
"test": [
1,
2,
3,
{
"test2": "123"
}
]
}
}
}
Expected result:
map[string][]string
map["data"][]string{""}
map["name"][]string{"John", "Johns"}
map["age"][]string{"30",""}
map["items"][]string{"1","2","3"}
map["test"][]string{"1","2","3"}
map["test2"][]string{"123"}
The solution is using recursive function.
https://en.wikipedia.org/wiki/Recursion_(computer_science)
Here is a simple example:
https://gobyexample.com/recursion
The following code can do what you want:
package main
import (
"encoding/json"
"fmt"
)
func main() {
JSONStr := `{
"data": {"name": "John", "age": 30},
"items": [1, 2, 3],
"data2": {
"name": "Johns",
"age": {"test": [1, 2, 3, {"test2": "123"}]}
}
}`
var JSON map[string]interface{}
json.Unmarshal([]byte(JSONStr), &JSON)
neededOutput := jsonToMap(JSON)
fmt.Println(neededOutput)
}
func jsonToMap(data map[string]interface{}) map[string][]string {
// final output
out := make(map[string][]string)
// check all keys in data
for key, value := range data {
// check if key not exist in out variable, add it
if _, ok := out[key]; !ok {
out[key] = []string{}
}
if valueA, ok := value.(map[string]interface{}); ok { // if value is map
out[key] = append(out[key], "")
for keyB, valueB := range jsonToMap(valueA) {
if _, ok := out[keyB]; !ok {
out[keyB] = []string{}
}
out[keyB] = append(out[keyB], valueB...)
}
} else if valueA, ok := value.([]interface{}); ok { // if value is array
for _, valueB := range valueA {
if valueC, ok := valueB.(map[string]interface{}); ok {
for keyD, valueD := range jsonToMap(valueC) {
if _, ok := out[keyD]; !ok {
out[keyD] = []string{}
}
out[keyD] = append(out[keyD], valueD...)
}
} else {
out[key] = append(out[key], fmt.Sprintf("%v", valueB))
}
}
} else { // if string and numbers and other ...
out[key] = append(out[key], fmt.Sprintf("%v", value))
}
}
return out
}
Output:
map[age:[30 ] data:[] data2:[] items:[1 2 3] name:[John Johns] test:[1 2 3] test2:[123]]

golang unmarshal map[string]interface{} to a struct containing an array with meta

I have the following json data coming through an API. I want to unmarshal this data into a different way of structure as it is defined below. How can I do it in an elegant way?
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
// It goes on..
}
type Data struct {
Meta Meta `json:"_meta,omitempty"`
Elements []Element
}
type Element struct {
Key string
Name string
}
type Meta struct{
Count int
Total int
}
This can be quite tricky because you have a json object that holds everything. So i went with the approach of unmarshalling to map of string to *json.RawMessage and then fixing the struct from there.
To do that you will be using a custom Unmarshaler and the benefit of it is that you delay the actual parsing of the inner messages until you need them.
So for example if your meta field was wrong or the numbers it said didn't match the length of the map-1 you could exit prematurely.
package main
import (
"encoding/json"
"fmt"
)
type jdata map[string]*json.RawMessage
type data struct {
Meta Meta
Elements []Element
}
//Element is a key val assoc
type Element struct {
Key string
Name string
}
//Meta holds counts and total of elems
type Meta struct {
Count int
Total int
}
var datain = []byte(`
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
}`)
func (d *data) UnmarshalJSON(buf []byte) (err error) {
var (
meta *json.RawMessage
ok bool
)
jdata := new(jdata)
if err = json.Unmarshal(buf, jdata); err != nil {
return
}
if meta, ok = (*jdata)["_meta"]; !ok {
return fmt.Errorf("_meta field not found in JSON")
}
if err = json.Unmarshal(*meta, &d.Meta); err != nil {
return
}
for k, v := range *jdata {
if k == "_meta" {
continue
}
elem := &Element{}
if err = json.Unmarshal(*v, elem); err != nil {
return err
}
d.Elements = append(d.Elements, *elem)
}
return nil
}
func main() {
data := &data{}
if err := data.UnmarshalJSON(datain); err != nil {
panic(err)
}
fmt.Printf("decoded:%v\n", data)
}

How can i put an object in object in golang

What i am trying to do is to load an whole object coming from my database into an object.
type RatingPK struct {
ratingsPK Rating
}
func (r *RatingPK) Init(rg Rating) {
r.ratingsPK = rg
}
func searchUserRatings(id string)(ratings []Rating) {
rows, err := db.Query("SELECT * FROM ratings WHERE userId = ?", id)
if err != nil {
return
}
for rows.Next() {
rate := Rating{}
err = rows.Scan(&rate.MovieId, &rate.UserId, &rate.RatingId, &rate.Rating, &rate.Timestamp)
if err != nil {
return
}
r := RatingPK{}
r.Init(rate)
ratings = append(ratings, r)
}
defer rows.Close()
return
}
this code gives me this result:
[{
"rating_id": 593,
"userId": 2,
"movieId": 93,
"rating": 3,
"timestamp": "0000-00-00"
},
{
"rating_id": 616,
"userId": 2,
"movieId": 94,
"rating": 3,
"timestamp": "0000-00-00"
}]
Now what i want to do is to get each object of the array and put in to another object and the desired result will be this:
[{"ratingsPK": {
"rating_id": 593,
"userId": 2,
"movieId": 93,
"rating": 3,
"timestamp": "0000-00-00"
}},
{"ratingsPK": {
"rating_id": 616,
"userId": 2,
"movieId": 94,
"rating": 3,
"timestamp": "0000-00-00"
}}]
Is there any way to achieve that?
You may loop and do as follows
type RatingPK struct {
ratingsPK Rating
}
func (r *RatingPK) Init(rg Rating) {
r.ratingsPK = rg
}
func searchUserRatings(id string) (ratings []RatingPK) {
rows, err := db.Query("SELECT * FROM ratings WHERE userId = ?", id)
if err != nil {
return
}
var myarray []string
for rows.Next() {
rate := Rating{}
err = rows.Scan(&rate.MovieId, &rate.UserId, &rate.RatingId, &rate.Rating, &rate.Timestamp)
if err != nil {
return
}
r := RatingPK{}
r.Init(rate)
ratings = append(ratings, r)
}
defer rows.Close()
return
}
Here's a short example that will help you understand how to use nested structs ;)
package main
import (
"fmt"
)
type nameType struct {
firstName string
lastName string
}
type student struct {
name nameType
age int
}
func main() {
bob := student{nameType{"bob", "dope"}, 18}
fmt.Println(bob.name.firstName)
}
You can test it up here ;)
https://play.golang.org/p/ft9PGrlrrx

Resources