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

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
}

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 why is value overwritten while implementing hierarchy tree?

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.

Compare a string against a struct field within a slice of structs (sort.Search golang)

type user struct {
name string
age int
}
data := []user{
{
name: "timothy",
age: 23,
},
{
name: "johnson",
age: 45,
},
{
name: "jason",
age: 44,
},
{
name: "handson",
age: 15,
},
{
name: "andrew",
age: 41,
},
}
fmt.Println(data)
val := "johnson"
found := false
i := sort.Search(len(data), func(i int) bool {
println(i)
return data[i].name == val
})
if i < len(data) && data[i].name == val {
found = true
}
fmt.Println(val, found)
I tried to search for a string within an array of structs that including some string fields, but couldn't get a match, for example using johnson or andrew. What is the problem? Thank you!
This sorts the data into order, then does a search on it.
I had to declare a type users which is an slice of user,
then implement Len/Cmp/Swap for users type
The sort.Search function uses >=
package main
import (
"fmt"
"sort"
)
type user struct {
name string
age int
}
type users []user
func (o users) Len() int { return len(o) }
func (o users) Less(i, j int) bool { return o[i].name < o[j].name }
func (o users) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func main() {
data := users{
{
name: "timothy",
age: 23,
},
{
name: "johnson",
age: 45,
},
{
name: "jason",
age: 44,
},
{
name: "handson",
age: 15,
},
{
name: "andrew",
age: 41,
},
}
sort.Sort(data)
fmt.Println(data)
val := "johnson"
found := false
i := sort.Search(len(data), func(i int) bool {
println(i)
return data[i].name >= val
})
if i < len(data) && data[i].name == val {
found = true
}
fmt.Println(val, found)
return
}

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