Skip to content

Instantly share code, notes, and snippets.

@Maxim-Filimonov
Last active December 28, 2015 06:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Maxim-Filimonov/7460291 to your computer and use it in GitHub Desktop.
Save Maxim-Filimonov/7460291 to your computer and use it in GitHub Desktop.
Parsing flat structure into a tree in Go %)
package parser
import (
"database/sql"
"fmt"
)
type Row struct {
MenuId int
Menu sql.NullString
Submenu sql.NullString
Item sql.NullString
Parent int
Child int
Position int
ChildType string
MenuRoot bool
ItemRoot bool
}
type Menu struct {
Id int
children []MenuNode
}
type MenuItem struct {
Id int
Name string
}
func (menu Menu) Children() []MenuNode {
return menu.children
}
func (menu Menu) SetChildren(children []MenuNode) {
menu.children = children
}
func (menuItem MenuItem) Children() []MenuNode {
return []MenuNode{}
}
func (menuItem MenuItem) SetChildren(children []MenuNode) {
}
type MenuNode interface {
Children() []MenuNode
SetChildren([]MenuNode)
}
func Parse(rows []Row) []Menu {
rootMenus := make([]*Menu, 0, len(rows))
menus := make(map[int][]*Menu)
children := make(map[int][]MenuNode)
for _, row := range rows {
if row.ChildType == "Menu" {
if row.MenuRoot == true {
rootMenus = append(rootMenus, &Menu{Id: row.Child})
} else {
index_menu := menus[row.Parent]
index_menu = append(index_menu, &Menu{Id: row.Child})
menus[row.Parent] = index_menu
}
} else if row.ChildType == "MenuItem" {
child_index := children[row.Parent]
child_index = append(child_index, MenuItem{Id: row.Child, Name: row.Item.String})
children[row.Parent] = child_index
}
}
fmt.Println("Rootmenus", rootMenus)
fmt.Println("Menus", menus)
fmt.Println("Children", children)
for i := range rootMenus {
menu := rootMenus[i]
menu_submenus := menus[menu.Id]
nodes_submenus := make([]MenuNode, len(menu_submenus))
for i, v := range menu_submenus {
nodes_submenus[i] = MenuNode(v)
}
menu.children = append(menu.children, nodes_submenus...)
menu_children := children[menu.Id]
nodes_children := make([]MenuNode, len(menu_children))
for i, v := range menu_children {
nodes_children[i] = MenuNode(v)
}
fmt.Println("Nodes children", nodes_children)
menu.children = append(menu.children, nodes_children...)
fmt.Println(menu.children)
}
for i := range menus {
m := menus[i]
for i := range m {
menu_ref := m[i]
menu_children := children[menu_ref.Id]
nodes_children := make([]MenuNode, len(menu_children))
for i, v := range menu_children {
nodes_children[i] = MenuNode(v)
}
menu_ref.children = append(menu_ref.children, nodes_children...)
}
}
fmt.Println(rootMenus[0].children)
result := make([]Menu, 0, len(rootMenus))
for _, elem := range rootMenus {
result = append(result, *elem)
}
return result
}
package parser
import (
"database/sql"
"testing"
)
func TestCanParseRootMenus(t *testing.T) {
testItemName := sql.NullString{String: "Test"}
rows := []Row{
Row{
Child: 1,
Parent: 34,
MenuRoot: true,
ChildType: "Menu",
},
Row{
Child: 2,
Parent: 1,
MenuRoot: false,
ChildType: "Menu",
},
Row{
ItemRoot: false,
Parent: 2,
ChildType: "MenuItem",
Item: testItemName,
Child: 3,
},
}
results := Parse(rows)
menu := results[0]
sub_menu := menu.Children()[0]
menu_item := sub_menu.Children()[0].(MenuItem)
if menu_item.Name != "Test" {
t.Error("Menu structure doesn't contain expected menu item")
}
}
func TestCanParseMenuItemBeforeParent(t *testing.T) {
testItemName := sql.NullString{String: "Test"}
rows := []Row{
Row{
ItemRoot: false,
Parent: 2,
ChildType: "MenuItem",
Item: testItemName,
Child: 3,
},
Row{
Child: 1,
Parent: 34,
MenuRoot: true,
ChildType: "Menu",
},
Row{
Child: 2,
Parent: 1,
MenuRoot: false,
ChildType: "Menu",
},
}
results := Parse(rows)
menu := results[0]
sub_menu := menu.Children()[0]
menu_item := sub_menu.Children()[0].(MenuItem)
if menu_item.Name != "Test" {
t.Error("Menu structure doesn't contain expected menu item")
}
}
func TestCanParseMenuItemBeforeRootParent(t *testing.T) {
testItemName := sql.NullString{String: "Test"}
rows := []Row{
Row{
ItemRoot: false,
Parent: 2,
ChildType: "MenuItem",
Item: testItemName,
Child: 3,
},
Row{
Child: 1,
Parent: 34,
MenuRoot: true,
ChildType: "Menu",
},
Row{
Child: 2,
Parent: 1,
MenuRoot: true,
ChildType: "Menu",
},
}
results := Parse(rows)
menu := results[0]
sub_menu := menu.Children()[0]
menu_item := sub_menu.Children()[0].(MenuItem)
if menu_item.Name != "Test" {
t.Error("Menu structure doesn't contain expected menu item")
}
}
func TestCanParseMultipleMenuItemsInDifferentMenus(t *testing.T) {
rows := []Row{
Row{
ItemRoot: false,
Parent: 2,
ChildType: "MenuItem",
Item: sql.NullString{String: "Sandwich"},
Child: 3,
},
Row{
ItemRoot: false,
Parent: 3,
ChildType: "MenuItem",
Item: sql.NullString{String: "Fish & Chips"},
Child: 3,
},
Row{
Child: 1,
Parent: 34,
MenuRoot: true,
ChildType: "Menu",
},
Row{
Child: 2,
Parent: 1,
MenuRoot: false,
ChildType: "Menu",
},
Row{
Child: 2,
Parent: 1,
MenuRoot: false,
ChildType: "Menu",
},
}
results := Parse(rows)
menu := results[0]
sub_menu := menu.Children()[0]
sandwich := sub_menu.Children()[0].(MenuItem)
if sandwich.Name != "Sandwich" {
t.Error("Sandwich should be under Snacks menu")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment