I am trying to solve an exercise from The go programming language book:
The starting code can be found here: exercise.
What I need to do:
Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal. Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.
Signature: func ElementByID(doc *html.Node, id string) *html.Node
What I did:
func ElementByID(doc *html.Node, id string) *html.Node {
if doc.Data == id {
fmt.Printf(" %s: %s\n", "found", doc.Data)
return doc
}
return nil
}
func startElement(n *html.Node) bool {
if n.Type == html.ElementNode {
if ElementById(n, "a") != nil {
return true
}
fmt.Printf("%*s<%s>\n", depth*2, "", n.Data)
depth++
}
return false
}
func endElement(n *html.Node) bool {
if n.Type == html.ElementNode {
if ElementById(n, "a") != nil {
return true
}
depth--
fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)
}
return false
}
Is the above right?, or I've missed something? How can I stop the traversal where element is found?
The forEachNode is the same, only the pre and post signature was changed to return a bool.
You can create a closure and "close" found node. Example below.
Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal.:
func forEachNode(n *html.Node, pre, post func(n *html.Node) bool) {
if pre != nil && !pre(n) {
return
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil && !post(n) {
return
}
}
Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.:
func ElementByID(doc *html.Node, id string) *html.Node {
var found *html.Node
pre := func(n *html.Node) bool {
for _, a := range n.Attr {
if a.Key == "id" && a.Val == id {
found = n // memorize matching node
return false // stop traversing
}
}
return true
}
forEachNode(doc, pre, nil)
return found
}
Related
I am trying to recursively parse a form data dictionary into an embedded struct.
It is kindof working, but eventually only one field gets set. I figured that the field keeps being overridden by the old struct, but need some help finding the solution.
Entrypoint of the function:
func FormDataToStruct(data map[string]string, s any) {
// Parse the form data into the struct
v := reflect.ValueOf(s)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("not a struct")
}
formParse(data, v, s)
}
Set the values on the topmost struct:
func formParse(data map[string]string, v reflect.Value, s any) {
for key, value := range data {
if hasField(v, key) {
println("Found field: " + key)
var val, err = TransformValue(s, key, value)
if err != nil {
panic(err)
}
SetValue(s, key, val)
delete(data, key)
}
}
// Set values of inner structs
for key, value := range data {
keys := strings.Split(key, "_")
// Parse the inner struct recursively with another function
recurseKeys(keys, value, v, s, s)
}
}
Struct looks like this:
type Me struct {
Name string
Age int
Friend struct {
Name string
Age int
}
}
Form data to parse looks like this:
map[name:John age:20 friend_name:Jane friend_age:20]
func recurseKeys(keys []string, value string, v reflect.Value, s any, parent any) {
if len(keys) == 1 {
// We are at the end of the keys
// Set the value
var val, err = TransformValue(s, keys[0], value)
if err != nil {
panic(err)
}
SetValue(s, keys[0], val)
return
}
// We are not at the end of the keys
// We need to iterate over the struct
for i := 0; i < v.NumField(); i++ {
if strings.EqualFold(v.Type().Field(i).Name, keys[0]) {
// We found the field
// Recurse with the next key
newS := reflect.New(v.Field(i).Type())
recurseKeys(keys[1:], value, v.Field(i), newS.Interface(), parent)
// Check if the field on the old struct is a pointer, if it is, we need to set the pointer
// If it is not a pointer, we need to set the value
if v.Field(i).Kind() == reflect.Ptr {
// Check if newS is a pointer
if newS.Kind() == reflect.Ptr {
v.Field(i).Set(newS)
} else {
v.Field(i).Set(newS.Elem())
}
} else {
// Check if newS is a pointer
if newS.Kind() == reflect.Ptr {
v.Field(i).Set(newS.Elem())
} else {
v.Field(i).Set(newS)
}
}
}
}
}
Running the above form data through the struct would result in the following output:
println(meInstance)
// {John 20 {Jane 0}}
Any help is very much appreciated!
EDIT
Go playground with minimal reproducible example:
https://go.dev/play/p/d5pIK3uQrUL
The issue with this code is that it creates a new struct and replaces it with the existing object. Therefore, you will always see the last assigned value.
The fixed version of the code will be like below:
func recurseKeys(keys []string, value string, v reflect.Value, s any, parent any) {
if len(keys) == 1 {
// We are at the end of the keys
// Set the value
var val, err = TransformValue(s, keys[0], value)
if err != nil {
panic(err)
}
SetValue(s, keys[0], val)
return
}
// We are not at the end of the keys
// We need to iterate over the struct
for i := 0; i < v.NumField(); i++ {
if strings.EqualFold(v.Type().Field(i).Name, keys[0]) {
// We found the field
// Recurse with the next key
if v.Field(i).IsZero() {
var newS reflect.Value
if v.Field(i).Kind() == reflect.Ptr {
newS = reflect.New(v.Field(i).Type().Elem()).Elem()
} else {
newS = reflect.New(v.Field(i).Type())
}
// Check if the field on the old struct is a pointer, if it is, we need to set the pointer
// If it is not a pointer, we need to set the value
if v.Field(i).Kind() == reflect.Ptr {
v.Field(i).Set(newS.Addr())
} else {
v.Field(i).Set(newS.Elem())
}
}
if v.Field(i).Kind() == reflect.Ptr {
recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Interface(), parent)
} else {
recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Addr().Interface(), parent)
}
}
}
}
This code accepts struct pointers as well.
Performance improvement tip: You may want to consider scanning the target object and creating a map of name -> Reflect value to reduce the number of loops.
Maintenance tip: It's better to consider using struct tags instead of reflecting the struct Variable name directly.
can you help with search func, it always returns nil and i cannot understand why
func BTreeSearchItem(root *TreeNode, elem string) *TreeNode {
if root == nil {
return nil
}
if root.Data < elem {
return BTreeSearchItem(root.Left, elem)
} else if root.Data > elem {
return BTreeSearchItem(root.Right, elem)
}
return root
}
Tried to do like this, but it returns 4 instead of 7
func BTreeSearchItem(root *TreeNode, elem string) *TreeNode {
if root == nil {
return nil
}
if root.Data < elem {
BTreeSearchItem(root.Left, elem)
} else if root.Data > elem {
BTreeSearchItem(root.Right, elem)
}
return root
}
Complete Code is
package main
import "fmt"
type TreeNode struct {
Left, Right, Parent *TreeNode
Data string
}
func BTreeSearchItem(root *TreeNode, elem string) *TreeNode {
if root == nil {
return nil
}
if root.Data < elem {
BTreeSearchItem(root.Left, elem)
} else if root.Data > elem {
BTreeSearchItem(root.Right, elem)
}
return root
}
func BTreeInsertData(root *TreeNode, data string) *TreeNode {
if root == nil {
return &TreeNode{Data: data}
}
if root.Data == data {
return nil
}
if root.Data > data {
if root.Left == nil {
root.Left = &TreeNode{Data: data}
}
return BTreeInsertData(root.Left, data)
}
if root.Data < data {
if root.Right == nil {
root.Right = &TreeNode{Data: data}
}
return BTreeInsertData(root.Right, data)
}
return root
}
func main() {
root := &TreeNode{Data: "4"}
BTreeInsertData(root, "1")
BTreeInsertData(root, "7")
BTreeInsertData(root, "5")
selected := BTreeSearchItem(root, "7")
fmt.Print("Item selected -> ")
if selected != nil {
fmt.Println(selected.Data)
} else {
fmt.Println("nil")
}
fmt.Print("Parent of selected item -> ")
if selected.Parent != nil {
fmt.Println(selected.Parent.Data)
} else {
fmt.Println("nil")
}
fmt.Print("Left child of selected item -> ")
if selected.Left != nil {
fmt.Println(selected.Left.Data)
} else {
fmt.Println("nil")
}
fmt.Print("Right child of selected item -> ")
if selected.Right != nil {
fmt.Println(selected.Right.Data)
} else {
fmt.Println("nil")
}
}
searched the internet and found a lot by methods, but i need as func. i'm beginner at programming, so it hard to me to understand
PlayGoundLink
My suggestion is check the value of root in main() before calling function BTreeSearchItem. What I suspect is root values is already nil in that stage itself.
okey, https://play.golang.org/p/gQmdYLfAvIY this code works, in BTreeInsertData forgot to add Parant and in BTreeSearchItem I mismatch Right and Left
I'm trying to parse an HTML page and print its links.
I'm going over the parsed Html tree recursively adding links to a slice of strings.
I'm missing something out as I get out of memory error
here is my code:
package parser
import (
"errors"
"io"
"golang.org/x/net/html"
)
//URLParser returns all the urls inside a html page
type URLParser struct {
}
//GetURLS returns all
func (URLParser) GetURLS(htmlInput io.Reader) (*[]string, error) {
result := []string{}
htmlRoot, err := html.Parse(htmlInput)
//result := make([]string, 1000)
if err != nil {
parserError := errors.New("html parser failed with error" + err.Error())
return nil, parserError
}
finalResult := traverseHTMLTree(htmlRoot, &result)
return finalResult, nil
}
func traverseHTMLTree(node *html.Node, result *[]string) *[]string {
if node == nil {
return nil
}
if isLinkElement(node) {
currlink, shouldUse := getURLAttrb(node.Attr)
if shouldUse {
*result = append(*result, currlink)
}
}
for currNode := node.FirstChild; currNode != nil; currNode = currNode.NextSibling {
currRest := traverseHTMLTree(currNode, result)
if currRest != nil {
*result = append(*currRest, *result...)
}
}
return result
}
func getURLAttrb(attr []html.Attribute) (string, bool) {
for i := 0; i < len(attr); i++ {
if attr[i].Key == "href" {
return attr[i].Val, true
}
}
return "", false
}
func isLinkElement(node *html.Node) bool {
if node.Type == html.ElementNode {
if node.Data == "a" {
return true
}
}
return false
}
When just trying to print the links to stdout it works perfectly, so it got to be something with the way I handle the slice..
For each node in the HTML tree, you're adding the contents of the results array to itself, recursively. That should grow pretty quickly.
Note that when you check if isLinkElement, you add the item to the result list.
Then for each element of the html tree, you append the contents of the results array to itself, doubling it.
You're passing a pointer to a slice. You're effectively using a single slice for the whole program, and keep adding to it. What you're returning from the traverseHTMLTree is the same slice, not a copy of it. So it keeps growing.
One way to fix it is: do not pass the pointer to the slice. Pass the current slice, update it, and return the new slice.
The code looks like:
func Contain(livesJSON []LiveJSON, single db.Live) bool {
for _, json := range livesJSON {
if json.Start == single.Time && json.Team == single.HomeTeamId {
return false
} else {
return true
}
}
}
I have return in both if and else.
There is no guarantee the loop body will be executed. This is the case if you pass nil or an empty slice for livesJSON. That way you would not return anything.
For that case, you must insert a return statement after the loop:
func Contain(livesJSON []LiveJSON, single db.Live) bool {
for _, json := range livesJSON {
if json.Start == single.Time && json.Team == single.HomeTeamId {
return false
} else {
return true
}
}
return false
}
I tried to make Trie data structures by Go Language, but somehow it stuck with References problem,
Here it is. http://play.golang.org/p/ASSGF5Oe9R
// Package main provides ...
package main
import "fmt"
type RootTrie []Trie
type Trie struct {
subtrie []Trie
index byte
}
func (trie *Trie) Insert(data string) *Trie {
if data != "" {
if trie.index == 0 {
trie.index = data[0]
}
if next := trie.containsIndex(data[1:]); next != nil {
//Problem Point
fmt.Println(string(data[1]), "found follwing", string(data[0]))
next.Insert(data[1:])
} else {
nt := &Trie{}
trie.subtrie = append(trie.subtrie, *nt.Insert(data[1:]))
}
}
return trie
}
func (trie *Trie) containsIndex(next string) *Trie {
if next != "" {
for _, st := range trie.subtrie {
if st.index == next[0] {
return &st
}
}
}
return nil
}
func main() {
t := &Trie{}
t = t.Insert("hanyang")
fmt.Println("result:", t)
t = t.Insert("hanyKk")
fmt.Println("result:", t)
t.Insert("hanyK")
}
The following problems happen in second "Insert",
the where I put, //Problem Point
I made containsIndex method for searching next linked trie, and it searched well actually.
But when I updated next property which containsIndex given, its not affected its mother struct trie though.
What I don't understand is I gave it reference type when returning containsIndex, but its still
act liked 'value copied', Why does it not affected its mother structure(trie)?
Thanks!
The problem is in method containsIndex. Golang range by default creates copy each element in slice and assigns copy of this value to st (in your example). Usually to preserve reference to element in slice you should use original slice and its index. In you case method containsIndex should look something like this:
func (trie *Trie) containsIndex(next string) *Trie {
if next != "" {
for i, st := range trie.subtrie {
if st.index == next[0] {
return &trie.subtrie[i]
}
}
}
return nil
}