diff --git a/25-functions-and-pointers/04-pass-by-value-semantics/main.go b/25-functions-and-pointers/04-pass-by-value-semantics/main.go index 7f1e1fe..a71d164 100644 --- a/25-functions-and-pointers/04-pass-by-value-semantics/main.go +++ b/25-functions-and-pointers/04-pass-by-value-semantics/main.go @@ -26,7 +26,7 @@ func main() { parsed, err := parse(p, in.Text()) if err != nil { fmt.Println(err) - break + return } p = update(p, parsed) diff --git a/25-functions-and-pointers/04-pass-by-value-semantics/parse.go b/25-functions-and-pointers/04-pass-by-value-semantics/parser.go similarity index 86% rename from 25-functions-and-pointers/04-pass-by-value-semantics/parse.go rename to 25-functions-and-pointers/04-pass-by-value-semantics/parser.go index 53a4696..9b402c9 100644 --- a/25-functions-and-pointers/04-pass-by-value-semantics/parse.go +++ b/25-functions-and-pointers/04-pass-by-value-semantics/parser.go @@ -13,7 +13,7 @@ import ( "strings" ) -// result stores metrics for a domain +// result stores the parsed result for a domain type result struct { domain string visits int @@ -28,10 +28,12 @@ type parser struct { lines int // number of parsed lines (for the error messages) } +// newParser constructs, initializes and returns a new parser func newParser() parser { return parser{sum: make(map[string]result)} } +// parse parses a log line and returns the parsed result with an error func parse(p parser, line string) (parsed result, err error) { fields := strings.Fields(line) if len(fields) != 2 { @@ -50,6 +52,7 @@ func parse(p parser, line string) (parsed result, err error) { return } +// update updates the parser for the given parsing result func update(p parser, parsed result) parser { domain, visits := parsed.domain, parsed.visits diff --git a/25-functions-and-pointers/08-log-parser-pointers/main.go b/25-functions-and-pointers/08-log-parser-pointers/main.go index a07dfb3..1736415 100644 --- a/25-functions-and-pointers/08-log-parser-pointers/main.go +++ b/25-functions-and-pointers/08-log-parser-pointers/main.go @@ -11,30 +11,44 @@ import ( "bufio" "fmt" "os" + "sort" + "strings" ) func main() { - in := bufio.NewScanner(os.Stdin) - p := newParser() + in := bufio.NewScanner(os.Stdin) for in.Scan() { - add(p, in.Text()) + parsed := parse(&p, in.Text()) + update(&p, parsed) } - for _, name := range p.domains { - // vis := p.sum[d.name] - d := p.sum[name] + summarize(p) + dumpErrs([]error{in.Err(), p.lerr}) +} - fmt.Printf("%-25s -> %d\n", d.name, d.visits) - } - fmt.Printf("\n%-25s -> %d\n", "TOTAL", p.total) +// summarize summarizes and prints the parsing result +func summarize(p parser) { + sort.Strings(p.domains) - if p.lerr != nil { - fmt.Println("> Err:", p.lerr) + fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS") + fmt.Println(strings.Repeat("-", 45)) + + for _, domain := range p.domains { + parsed := p.sum[domain] + fmt.Printf("%-30s %10d\n", domain, parsed.visits) } - if err := in.Err(); err != nil { - fmt.Println("> Err:", err) + // Print the total visits for all domains + fmt.Printf("\n%-30s %10d\n", "TOTAL", p.total) +} + +// dumpErrs simplifies handling multiple errors +func dumpErrs(errs []error) { + for _, err := range errs { + if err != nil { + fmt.Println("> Err:", err) + } } } diff --git a/25-functions-and-pointers/08-log-parser-pointers/parser.go b/25-functions-and-pointers/08-log-parser-pointers/parser.go index 45b270d..fcfd714 100644 --- a/25-functions-and-pointers/08-log-parser-pointers/parser.go +++ b/25-functions-and-pointers/08-log-parser-pointers/parser.go @@ -1,3 +1,10 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + package main import ( @@ -6,94 +13,71 @@ import ( "strings" ) -// TODO: add add() func -// TODO: add error handling (variadics) -// TODO: add iterator func values -// TODO: add summarizer to main() - -// domain represents a single domain log record -type domain struct { - name string +// result stores the parsed result for a domain +type result struct { + domain string visits int + // add more metrics if needed } -// parser parses a log file and provides an iterator to iterate upon the domains -// -// the parser struct is carefully crafted to be usable using its zero values except the map field +// parser keep tracks of the parsing type parser struct { - // sum map[string]int // visits per unique domain - // domains []domain // unique domain names - sum map[string]domain // visits per unique domain + sum map[string]result // metrics per domain domains []string // unique domain names - - total int // total visits to all domains - lines int // number of parsed lines (for the error messages) - lerr error // saves the last error occurred + total int // total visits for all domains + lines int // number of parsed lines (for the error messages) + lerr error // the last error occurred } -// newParser creates and returns a new parser. -func newParser() *parser { - // return &parser{sum: make(map[string]int)} - return &parser{sum: make(map[string]domain)} +// newParser constructs, initializes and returns a new parser +func newParser() parser { + return parser{sum: make(map[string]result)} } -// add parses the given line and saves the result to the internal list of -// domains. it doesn't add the record when the parsing fails. -func add(p *parser, line string) { - // if there was a previous error do not add +// parse parses a log line and returns the parsed result with an error +func parse(p *parser, line string) (parsed result) { if p.lerr != nil { return } - dom, err := parse(p, line) - - // store only the last error - if err != nil { - p.lerr = err - return - } - - push(p, dom) -} - -// parse parses the given text and returns a domain struct -func parse(p *parser, line string) (dom domain, err error) { p.lines++ fields := strings.Fields(line) if len(fields) != 2 { - err = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines) + p.lerr = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines) return } - dom.name = fields[0] + parsed.domain = fields[0] - dom.visits, err = strconv.Atoi(fields[1]) - if dom.visits < 0 || err != nil { - err = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines) + var err error + + parsed.visits, err = strconv.Atoi(fields[1]) + if parsed.visits < 0 || err != nil { + p.lerr = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines) } - return } -// push pushes the given domain to the internal list of domains. -// it also increases the total visits for all the domains. -func push(p *parser, d domain) { - // TODO: - // if _, ok := p.sum[d.name]; !ok { - // p.domains = append(p.domains, d) - // } - - // p.sum[d.name] += d.visits - // p.total += d.visits - name := d.name - - // collect the unique domains - if _, ok := p.sum[name]; !ok { - p.domains = append(p.domains, name) +// update updates the errors for the given parsing result +func update(p *parser, parsed result) { + if p.lerr != nil { + return } - p.total += d.visits - d.visits += p.sum[name].visits - p.sum[name] = d + domain, visits := parsed.domain, parsed.visits + + // Collect the unique domains + if _, ok := p.sum[domain]; !ok { + p.domains = append(p.domains, domain) + } + + // Keep track of total and per domain visits + p.total += visits + + // create and assign a new copy of `visit` + p.sum[domain] = result{ + domain: domain, + visits: visits + p.sum[domain].visits, + } } diff --git a/25-functions-and-pointers/08x-log-parser-pointers/main.go b/25-functions-and-pointers/08x-log-parser-pointers/main.go deleted file mode 100644 index 13247a6..0000000 --- a/25-functions-and-pointers/08x-log-parser-pointers/main.go +++ /dev/null @@ -1,57 +0,0 @@ -// For more tutorials: https://blog.learngoprogramming.com -// -// Copyright © 2018 Inanc Gumus -// Learn Go Programming Course -// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ -// - -package main - -import ( - "bufio" - "fmt" - "os" -) - -func main() { - in := bufio.NewScanner(os.Stdin) - - p := newParser() - for in.Scan() { - // dom, err := parse(p, in.Text()) - // if err != nil { - // fmt.Println(err) - // return - // } - - // p = push(p, dom) - - add(p, in.Text()) - } - - summarize(p) - dumpErrs(in.Err(), err(p)) -} - -// funcs not always need to be reused. -// here, it tells about what it does: it summarizes the parsing result. -func summarize(p *parser) { - // multiple iterators can be created. each one remembers the last - // read domain record. - - next, cur := iterator(p) - for next() { - dom := cur() - fmt.Printf("%-25s -> %d\n", dom.name, dom.visits) - } - fmt.Printf("\n%-25s -> %d\n", "TOTAL", p.total) -} - -// this variadic func simplifies the multiple error handling -func dumpErrs(errs ...error) { - for _, err := range errs { - if err != nil { - fmt.Printf("> Err: %s\n", err) - } - } -} diff --git a/25-functions-and-pointers/08x-log-parser-pointers/parser.go b/25-functions-and-pointers/08x-log-parser-pointers/parser.go deleted file mode 100644 index 05d9e3b..0000000 --- a/25-functions-and-pointers/08x-log-parser-pointers/parser.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "fmt" - "strconv" - "strings" -) - -// domain represents a domain log record -type domain struct { - name string - visits int -} - -// parser parses a log file and provides an iterator to iterate upon the domains -// -// the parser struct is carefully crafted to be usable using its zero values except the map field -type parser struct { - sum map[string]domain // visits per unique domain - domains []string // unique domain names - total int // total visits to all domains - lines int // number of parsed lines (for the error messages) - lerr error // saves the last error occurred -} - -// newParser creates and returns a new parser. -func newParser() *parser { - return &parser{sum: make(map[string]domain)} -} - -// add parses the given line and saves the result to the internal list of -// domains. it doesn't add the record when the parsing fails. -func add(p *parser, line string) { - // if there was a previous error do not add - if p.lerr != nil { - return - } - - dom, err := parse(p, line) - - // store only the last error - if err != nil { - p.lerr = err - return - } - - push(p, dom) -} - -// iterator returns two functions for iterating over domains. -// next = returns true when there are more domains to iterate on. -// cur = returns the current domain -func iterator(p *parser) (next func() bool, cur func() domain) { - // remember the last received line - var last int - - next = func() bool { - defer func() { last++ }() - return len(p.domains) > last - } - - cur = func() domain { - // return a copy so the caller cannot change it - name := p.domains[last-1] - return p.sum[name] - } - - return -} - -// error returns the last error occurred -func err(p *parser) error { - return p.lerr -} - -// parse parses the given text and returns a domain struct -func parse(p *parser, line string) (dom domain, err error) { - p.lines++ // increase the parsed line counter (only write is here) - - fields := strings.Fields(line) - if len(fields) != 2 { - err = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines) - return - } - - name, visits := fields[0], fields[1] - - n, err := strconv.Atoi(visits) - if n < 0 || err != nil { - err = fmt.Errorf("wrong input: %q (line #%d)", visits, p.lines) - return - } - - return domain{name: name, visits: n}, nil -} - -// push pushes the given domain to the internal list of domains. -// it also increases the total visits for all the domains. -func push(p *parser, d domain) { - name := d.name - - // collect the unique domains - if _, ok := p.sum[name]; !ok { - p.domains = append(p.domains, name) - } - - p.total += d.visits - d.visits += p.sum[name].visits - p.sum[name] = d -} diff --git a/25-functions-and-pointers/08x-log-parser-pointers/log.txt b/25-functions-and-pointers/09-log-parser-pointers-vs-values/log.txt similarity index 100% rename from 25-functions-and-pointers/08x-log-parser-pointers/log.txt rename to 25-functions-and-pointers/09-log-parser-pointers-vs-values/log.txt diff --git a/25-functions-and-pointers/08x-log-parser-pointers/log_err_missing.txt b/25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_missing.txt similarity index 100% rename from 25-functions-and-pointers/08x-log-parser-pointers/log_err_missing.txt rename to 25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_missing.txt diff --git a/25-functions-and-pointers/08x-log-parser-pointers/log_err_negative.txt b/25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_negative.txt similarity index 100% rename from 25-functions-and-pointers/08x-log-parser-pointers/log_err_negative.txt rename to 25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_negative.txt diff --git a/25-functions-and-pointers/08x-log-parser-pointers/log_err_str.txt b/25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_str.txt similarity index 100% rename from 25-functions-and-pointers/08x-log-parser-pointers/log_err_str.txt rename to 25-functions-and-pointers/09-log-parser-pointers-vs-values/log_err_str.txt diff --git a/25-functions-and-pointers/09-log-parser-pointers-vs-values/main.go b/25-functions-and-pointers/09-log-parser-pointers-vs-values/main.go new file mode 100644 index 0000000..57cebd8 --- /dev/null +++ b/25-functions-and-pointers/09-log-parser-pointers-vs-values/main.go @@ -0,0 +1,51 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "bufio" + "fmt" + "os" + "sort" + "strings" +) + +func main() { + p := newParser() + + in := bufio.NewScanner(os.Stdin) + for in.Scan() { + update(p, parse(p, in.Text())) + } + + summarize(p) + dumpErrs([]error{in.Err(), err(p)}) +} + +// summarize summarizes and prints the parsing result +func summarize(p *parser) { + sort.Strings(p.domains) + + fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS") + fmt.Println(strings.Repeat("-", 45)) + + for _, domain := range p.domains { + parsed := p.sum[domain] + fmt.Printf("%-30s %10d\n", domain, parsed.visits) + } + fmt.Printf("\n%-30s %10d\n", "TOTAL", p.total) +} + +// dumpErrs simplifies handling multiple errors +func dumpErrs(errs []error) { + for _, err := range errs { + if err != nil { + fmt.Println("> Err:", err) + } + } +} diff --git a/25-functions-and-pointers/09-log-parser-pointers-vs-values/parser.go b/25-functions-and-pointers/09-log-parser-pointers-vs-values/parser.go new file mode 100644 index 0000000..e781eaa --- /dev/null +++ b/25-functions-and-pointers/09-log-parser-pointers-vs-values/parser.go @@ -0,0 +1,88 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "strconv" + "strings" +) + +// result stores the parsed result for a domain +type result struct { + domain string + visits int + // add more metrics if needed +} + +// parser keep tracks of the parsing +type parser struct { + sum map[string]result // metrics per domain + domains []string // unique domain names + total int // total visits for all domains + lines int // number of parsed lines (for the error messages) + lerr error // the last error occurred +} + +// newParser constructs, initializes and returns a new parser +func newParser() *parser { + return &parser{sum: make(map[string]result)} +} + +// parse parses a log line and returns the parsed result with an error +func parse(p *parser, line string) (parsed result) { + if p.lerr != nil { + return + } + + p.lines++ + + fields := strings.Fields(line) + if len(fields) != 2 { + p.lerr = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines) + return + } + + parsed.domain = fields[0] + + var err error + + parsed.visits, err = strconv.Atoi(fields[1]) + if parsed.visits < 0 || err != nil { + p.lerr = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines) + } + return +} + +// update updates the errors for the given parsing result +func update(p *parser, parsed result) { + if p.lerr != nil { + return + } + + domain, visits := parsed.domain, parsed.visits + + // Collect the unique domains + if _, ok := p.sum[domain]; !ok { + p.domains = append(p.domains, domain) + } + + // Keep track of total and per domain visits + p.total += visits + + // create and assign a new copy of `visit` + p.sum[domain] = result{ + domain: domain, + visits: visits + p.sum[domain].visits, + } +} + +// err returns the last error encountered +func err(p *parser) error { + return p.lerr +} diff --git a/25-functions-and-pointers/_WIP/log.txt b/25-functions-and-pointers/_WIP/log.txt deleted file mode 100644 index fb2432b..0000000 --- a/25-functions-and-pointers/_WIP/log.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org 4 -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/25-functions-and-pointers/_WIP/log_err_missing.txt b/25-functions-and-pointers/_WIP/log_err_missing.txt deleted file mode 100644 index fd8eff4..0000000 --- a/25-functions-and-pointers/_WIP/log_err_missing.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/25-functions-and-pointers/_WIP/log_err_negative.txt b/25-functions-and-pointers/_WIP/log_err_negative.txt deleted file mode 100644 index 60485c0..0000000 --- a/25-functions-and-pointers/_WIP/log_err_negative.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org -100 -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/25-functions-and-pointers/_WIP/log_err_str.txt b/25-functions-and-pointers/_WIP/log_err_str.txt deleted file mode 100644 index 3a55bd7..0000000 --- a/25-functions-and-pointers/_WIP/log_err_str.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org FOUR -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/log-parser-json/log.txt b/x-tba/log-parser-json/log.txt deleted file mode 100644 index fb2432b..0000000 --- a/x-tba/log-parser-json/log.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org 4 -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/log-parser-json/log_err_missing.txt b/x-tba/log-parser-json/log_err_missing.txt deleted file mode 100644 index fd8eff4..0000000 --- a/x-tba/log-parser-json/log_err_missing.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/log-parser-json/log_err_negative.txt b/x-tba/log-parser-json/log_err_negative.txt deleted file mode 100644 index 60485c0..0000000 --- a/x-tba/log-parser-json/log_err_negative.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org -100 -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/log-parser-json/log_err_str.txt b/x-tba/log-parser-json/log_err_str.txt deleted file mode 100644 index 3a55bd7..0000000 --- a/x-tba/log-parser-json/log_err_str.txt +++ /dev/null @@ -1,6 +0,0 @@ -learngoprogramming.com 10 -learngoprogramming.com 10 -golang.org FOUR -golang.org 6 -blog.golang.org 20 -blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/log-parser-json/main.go b/x-tba/log-parser-json/main.go deleted file mode 100644 index 903cf83..0000000 --- a/x-tba/log-parser-json/main.go +++ /dev/null @@ -1,97 +0,0 @@ -// For more tutorials: https://blog.learngoprogramming.com -// -// Copyright © 2018 Inanc Gumus -// Learn Go Programming Course -// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ -// - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "os" - "sort" - "strconv" - "strings" -) - -// Visit stores metrics for a domain -type Visit struct { - Domain string `json:"domain"` - Total int `json:"total_visits"` - // add more metrics if needed -} - -// Parser keep tracks of the parsing -type Parser struct { - Sum map[string]Visit `json:"visits_per_domain"` // metrics per domain - Domains []string `json:"unique_domains"` // unique domain names - Total int `json:"total_visits"` // total visits for all domains - Lines int `json:"-"` // number of parsed lines (for the error messages) -} - -func main() { - p := Parser{Sum: make(map[string]Visit)} - - // Scan the standard-in line by line - in := bufio.NewScanner(os.Stdin) - for in.Scan() { - p.Lines++ - - // Parse the fields - fields := strings.Fields(in.Text()) - if len(fields) != 2 { - fmt.Printf("wrong input: %v (line #%d)\n", fields, p.Lines) - return - } - - // Sum the total visits per domain - visits, err := strconv.Atoi(fields[1]) - if visits < 0 || err != nil { - fmt.Printf("wrong input: %q (line #%d)\n", fields[1], p.Lines) - return - } - - name := fields[0] - - // Collect the unique domains - if _, ok := p.Sum[name]; !ok { - p.Domains = append(p.Domains, name) - } - - // Keep track of total and per domain visits - p.Total += visits - - // You cannot assign to composite values - // p.Sum[name].Total += visits - - // create and assign a new copy of `visit` - p.Sum[name] = Visit{ - Domain: name, - Total: visits + p.Sum[name].Total, - } - } - - // Print the visits per domain - sort.Strings(p.Domains) - - // fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS") - // fmt.Println(strings.Repeat("-", 45)) - - // for _, name := range p.Domains { - // visits := p.Sum[name] - // fmt.Printf("%-30s %10d\n", name, visits.Total) - // } - - // // Print the total visits for all domains - // fmt.Printf("\n%-30s %10d\n", "TOTAL", p.Total) - - // if err := in.Err(); err != nil { - // fmt.Println("> Err:", err) - // } - - s, _ := json.MarshalIndent(p, "", "\t") - fmt.Println(string(s)) -}