Skip to content

Instantly share code, notes, and snippets.

@dlford
Created August 23, 2023 23:27
Show Gist options
  • Save dlford/9e66069cfc7fb9afc649c5e3dc650083 to your computer and use it in GitHub Desktop.
Save dlford/9e66069cfc7fb9afc649c5e3dc650083 to your computer and use it in GitHub Desktop.
Example Go Code
package main
import (
"encoding/json"
"fmt"
)
// Comment from Database
type Comment struct {
ID string `json:"id""`
ParentID string `json:"pid"`
Text string `json:"text"`
}
// NestedComment output
type NestedComment struct {
Comment
Children []NestedComment `json:"children,omitempty"`
}
func main() {
// Some comment data from DB
jsondata := `[{"id":"f1c3a838-f4b4-4ebd-bb1b-d3e4c0dce27d","pid":"6a162b3f-9341-46e9-95f7-dedc6cd06868","text":"comment 1"},{"id":"131cf4bb-1971-43a2-992f-2cbb042a4184","pid":"","text":"comment 2"},{"id":"4c93a789-ecab-4173-954c-b039cd3a9e17","pid":"131cf4bb-1971-43a2-992f-2cbb042a4184","text":"comment 3"},{"id":"c7974b6d-67e1-47d0-84fa-c78d51682df2","pid":"131cf4bb-1971-43a2-992f-2cbb042a4184","text":"comment 4"},{"id":"386c4439-4596-47c1-99bf-cb479055fe5a","pid":"","text":"comment 5"},{"id":"5ef0816e-ea13-4a7e-8009-e67ee08ef906","pid":"386c4439-4596-47c1-99bf-cb479055fe5a","text":"comment 6"},{"id":"96b0a536-602b-4f3f-a352-020f22cdfb18","pid":"","text":"comment 7"},{"id":"d0f11e98-deea-4fd5-becb-8f406ebcae83","pid":"96b0a536-602b-4f3f-a352-020f22cdfb18","text":"comment 8"},{"id":"bad87461-3cbf-4ecb-a8c7-36876b57d652","pid":"","text":"comment 9"},{"id":"e3ee6f90-495f-4348-9c29-bd6e15ec3b39","pid":"bad87461-3cbf-4ecb-a8c7-36876b57d652","text":"comment 10"},{"id":"6a162b3f-9341-46e9-95f7-dedc6cd06868","pid":"","text":"comment 11"},{"id":"041c7c2d-0688-491e-b6cd-899eac95bd3d","pid":"","text":"comment 12"},{"id":"5acdf3ff-345b-47a5-bd66-a6687a1441b7","pid":"041c7c2d-0688-491e-b6cd-899eac95bd3d","text":"comment 13"},{"id":"aaee2ae5-7e3a-4435-a094-83bf6b77abfb","pid":"","text":"comment 14"},{"id":"6108b28c-a7af-4c5a-a38c-c22db0d34b31","pid":"aaee2ae5-7e3a-4435-a094-83bf6b77abfb","text":"comment 15"},{"id":"1736393e-95b0-4f5d-a149-50fe52c28350","pid":"","text":"comment 16"},{"id":"b646f202-17aa-4af9-b8dc-c1d8ff3529ac","pid":"1736393e-95b0-4f5d-a149-50fe52c28350","text":"comment 17"},{"id":"a8827b1d-fdbc-41c2-bdb5-38e8b308dedc","pid":"b646f202-17aa-4af9-b8dc-c1d8ff3529ac","text":"comment 18"},{"id":"76e49597-6039-4cb5-a595-556ccc2e3c12","pid":"a8827b1d-fdbc-41c2-bdb5-38e8b308dedc","text":"comment 19"},{"id":"07dc4b90-65d3-4f75-8351-457305988235","pid":"76e49597-6039-4cb5-a595-556ccc2e3c12","text":"comment 20"},{"id":"2d4e838b-82df-4ca0-bf9b-f1dfb99d6763","pid":"07dc4b90-65d3-4f75-8351-457305988235","text":"comment 21"},{"id":"617c1760-0dca-409c-af2a-c614f0530e07","pid":"","text":"comment 22"},{"id":"712c0b7e-a9e4-40fd-ab78-c39b483b1435","pid":"617c1760-0dca-409c-af2a-c614f0530e07","text":"comment 23"},{"id":"e325094c-886e-42c9-bd59-fdd7a189b384","pid":"","text":"comment 24"},{"id":"f3b8b0f9-edef-4ee6-9555-42e8595f854c","pid":"e325094c-886e-42c9-bd59-fdd7a189b384","text":"comment 25"},{"id":"6e09a231-a2ee-4aa1-a898-57695e6fe153","pid":"","text":"comment 26"},{"id":"63e629d6-c0f9-4b36-ad96-e0c47cf8bb4e","pid":"6e09a231-a2ee-4aa1-a898-57695e6fe153","text":"comment 27"},{"id":"d7b698f1-8757-46db-a6e6-5141a481b42b","pid":"","text":"comment 28"},{"id":"a7e3d522-553c-44f1-befa-047bd94f0e59","pid":"","text":"comment 29"},{"id":"fe78ce24-c2c8-4d00-b96d-8b8be9ce0f06","pid":"","text":"comment 30"},{"id":"fabdccb5-a024-4476-bd51-7d55fb685c42","pid":"","text":"comment 31"},{"id":"608d0693-6e43-46a0-9e54-107da6adb109","pid":"fabdccb5-a024-4476-bd51-7d55fb685c42","text":"comment 32"}]`
var comments []Comment
err := json.Unmarshal([]byte(jsondata), &comments)
if err != nil {
panic(err)
}
arranged := ArrangeComments(comments)
arrangedjson, _ := json.MarshalIndent(arranged, "", " ")
fmt.Println(string(arrangedjson))
fmt.Println("comments", len(comments), "arranged", recursiveCount(arranged))
}
func recursiveCount(arranged []NestedComment) int {
count := len(arranged)
for _, item := range arranged {
count += recursiveCount(item.Children)
}
return count
}
// Nesting function -------------------------------------------------------------------------
func ArrangeComments(comments []Comment) []NestedComment {
// Map to hold references to NestedComment by their ID
queue := make(map[string]*NestedComment)
// Result slice
var result []NestedComment
// First, create all NestedComments and store references in the map
for _, comment := range comments {
nc := &NestedComment{Comment: comment}
queue[comment.ID] = nc
}
// Second, loop through the map and add children to their parents
for _, comment := range queue {
if comment.ParentID != "" {
parent := queue[comment.Comment.ParentID]
if parent != nil {
parent.Children = append(parent.Children, *comment)
}
}
}
// Third, loop through the map and add all root comments to the result slice
for _, comment := range queue {
if comment.ParentID == "" {
result = append(result, *comment)
}
}
return result
}
@MysteriousPotato
Copy link

The issue is that you're dereferencing the comment ptr at line 68.

This will have the effect of only storing the value of comment, not its reference.

So when you append a comment to it's children later on, this change is not reflected on the value previously stored in its parent's Children.

Using pointers to store the children should resolve the issue.

@dlford
Copy link
Author

dlford commented Aug 24, 2023

@MysteriousPotato you mean like this?

    for _, comment := range queue {
        if comment.ParentID != "" {
            if queue[comment.Comment.ParentID] != nil {
                queue[comment.Comment.ParentID].Children = append(queue[comment.Comment.ParentID].Children, *comment)
            }
        }
    }

Same output, what am I doing wrong?

@MysteriousPotato
Copy link

MysteriousPotato commented Aug 24, 2023

I mean using []*NestedComment instead of []NestedComment for storing children as pointers.

If you don't use pointers, any changes made to the children won't affect the child previously stored in its parent's children.

Ex.:

func main() {	
        type myStruct struct {
		value int
	}

	test := myStruct{}

	ptr := &test
	ptr.value = 1
	fmt.Println(test.value) // prints 1

	// This will effectively create a copy of the value stored in `ptr`
	// So any changes made to `ptr` won't affect `dereferenced` since it's a value copy, not a reference.
	dereferenced := *ptr
	ptr.value = 2
	fmt.Println(dereferenced.value) // still prints 1
}

@dlford
Copy link
Author

dlford commented Aug 24, 2023

@MysteriousPotato that was it, thank you so much!

@dlford
Copy link
Author

dlford commented Aug 24, 2023

@MysteriousPotato do you have a BuyMeACoffee or something? I'd like to show my appreciation

@MysteriousPotato
Copy link

That's very generous of you. I don't have a BuyMeACoffee but here's an idea:

You could donate to a charity of your choice! At the end, it's up to you. I'm just glad I could help.

@dlford
Copy link
Author

dlford commented Aug 24, 2023

@MysteriousPotato Great idea, I gave you a shout-out in my donation, thanks again!

ksnip_20230824-124349

@MysteriousPotato
Copy link

@dlford 🎉 Great choice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment