golang why is value overwritten while implementing hierarchy tree? - go

package main
import (
"encoding/json"
"fmt"
)
type employee struct {
Name string `json:"name"`
Id int `json:"id,omitempty"`
ManagerId int `json:"managerid"`
Reporters []employee `json:"reporters,omitempty"`
}
// type employees struct {
// employees []employee `json:"employee"`
// }
type EmployeeList struct {
employees map[int]employee
root employee
}
func NewEmployeeList() *EmployeeList {
var el EmployeeList
el.employees = make(map[int]employee)
return &el
}
func (el *EmployeeList) CreateMap(employeesInfo []employee) {
for _ , emp := range employeesInfo {
e := employee{
Name : emp.Name,
Id: emp.Id,
ManagerId: emp.ManagerId,
}
el.employees[emp.Id] = e
if(emp.ManagerId == 0){
el.root = e
fmt.Println("CreateMap",el.root)
}
}
fmt.Println("CreateMap2",el.root,el.employees)
}
func (el *EmployeeList) getReportersById(empId int) []employee {
reporters := []employee{}
for _ , employee := range el.employees {
if(employee.ManagerId == empId){
reporters = append(reporters, employee)
}
}
return reporters
}
func (el *EmployeeList) maketree(e *employee) {
//e := root
e.Reporters = el.getReportersById(e.Id)
if(true){
fmt.Println("maketree",e.Id,e.Name,e.Reporters)
}
// e.Reporters = reporters
if(len(e.Reporters) == 0){
return
}
for _ , reporterEmployee := range e.Reporters {
el.maketree(&reporterEmployee);
}
}
func (el EmployeeList) print(root employee, level int) {
for i:= 0; i<level;i++ {
fmt.Print("\t");
}
fmt.Println(root.Name);
for _, reporter := range root.Reporters {
el.print(reporter, level + 1)
}
}
func main() {
//1. Read JSON File
myJsonString := `[{ "name": "Rob", "id": 7, "managerid": 3 }, { "name": "Rex", "id": 6, "managerid": 2 }, { "name": "Jake", "id": 5, "managerid": 2 }, { "name": "Paul", "id": 4, "managerid": 1 }, { "name": "Oliver", "id": 3, "managerid": 1 }, { "name": "John", "id": 2, "managerid": 1 }, { "name": "Britney", "id": 1, "managerid": 0 }]`
//2. Create class and sent file data
emplist := NewEmployeeList()
rawEmployeesInfo := []employee{}
_ = json.Unmarshal([]byte(myJsonString),&rawEmployeesInfo);
//fmt.Println(rawEmployeesInfo);
emplist.CreateMap(rawEmployeesInfo);
//fmt.Println(emplist.employees,emplist.root);
fmt.Println("Main1",emplist.root)
emplist.maketree(&emplist.root);
//fmt.Println(emplist.root)
fmt.Println("Main2",emplist.root)
emplist.print(emplist.root,0)
}
I am trying to create a tree from a json to store employee list. The problem is that while creating the tree values are stored correctly but somehow they lose value in the main function when I want to print the hierarchy tree. Can someone please help me why the value is not stored? I have added logs also to check.

Your CreateMap method:
func (el EmployeeList) CreateMap(employeesInfo []employee) {
...
}
should be:
func (el *EmployeeList) CreateMap(employeesInfo []employee) {
//---^
....
}
so that el is a pointer. Otherwise you're operating on a copy of emplist when you say:
emplist.CreateMap(rawEmployeesInfo);
so you do a bunch of work to create your map and then throw it all away.

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

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

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

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
}

Convert JSON key value pair into single field value in Go

I have a json like following, where value can be int or string
{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}
Now I want to convert that json into following struct, like only extract a_name and b_name value.
type Data struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
I can do it by following way
import (
"encoding/json"
"fmt"
)
type TmpData struct {
Data []struct {
Name string `json:"Name"`
Value interface{} `json:"value"`
} `json:"data"`
}
type ExpectedData struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
func main() {
data := `{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}`
tmpData := &TmpData{}
json.Unmarshal([]byte(data), tmpData)
ans := &ExpectedData{}
for _, d := range tmpData.Data {
if d.Name == "a_name" {
ans.AName = int(d.Value.(float64))
} else if d.Name == "b_name" {
ans.BName = d.Value.(string)
}
}
fmt.Println(ans)
}
Is there any better solution for this?
Not possible with the standard JSON un-marshalling unless you write a custom un-marshaller for your Data type.
The key here is to define the type for value to be an interface{}, so that multiple types could be stored in your b_name record.
func (d *Data) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _, value := range result.Data {
switch value.Name {
// The json package will assume float64 when Unmarshalling with an interface{}
case "a_name":
v, ok := (value.Value).(float64)
if !ok {
return fmt.Errorf("a_name got data of type %T but wanted float64", value.Value)
}
d.AName = int(v)
case "b_name":
v, ok := (value.Value).(string)
if !ok {
return fmt.Errorf("b_name got data of type %T but wanted string", value.Value)
}
d.BName = v
}
}
return nil
}
Playground - https://go.dev/play/p/GrXKAE87d1F

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)
}

Resources