move: advanced funcs to functional programming
This commit is contained in:
20
27-functional-programming/log-parser-exp/field.go
Normal file
20
27-functional-programming/log-parser-exp/field.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// field helps for field parsing
|
||||||
|
type field struct{ err error }
|
||||||
|
|
||||||
|
// uatoi parses an unsigned integer string and saves the error.
|
||||||
|
// it assumes that the val is unsigned.
|
||||||
|
// for ease of usability: it returns an int instead of uint.
|
||||||
|
func (f *field) uatoi(name, val string) int {
|
||||||
|
n, err := strconv.Atoi(val)
|
||||||
|
if n < 0 || err != nil {
|
||||||
|
f.err = fmt.Errorf("incorrect field -> %q = %q", name, val)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
15
27-functional-programming/log-parser-exp/filterby.go
Normal file
15
27-functional-programming/log-parser-exp/filterby.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func filterBy(results []result, filterer filterFunc) []result {
|
||||||
|
out := results[:0]
|
||||||
|
|
||||||
|
for _, r := range results {
|
||||||
|
if !filterer(r) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
34
27-functional-programming/log-parser-exp/filters.go
Normal file
34
27-functional-programming/log-parser-exp/filters.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func noopFilter(r result) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func not(filter filterFunc) filterFunc {
|
||||||
|
return func(r result) bool {
|
||||||
|
return !filter(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func domainExtFilter(domains ...string) filterFunc {
|
||||||
|
return func(r result) bool {
|
||||||
|
for _, domain := range domains {
|
||||||
|
if strings.HasSuffix(r.domain, "."+domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func domainFilter(domain string) filterFunc {
|
||||||
|
return func(r result) bool {
|
||||||
|
return strings.Contains(r.domain, domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func orgDomainsFilter(r result) bool {
|
||||||
|
return strings.HasSuffix(r.domain, ".org")
|
||||||
|
}
|
17
27-functional-programming/log-parser-exp/groupby.go
Normal file
17
27-functional-programming/log-parser-exp/groupby.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func groupBy(results []result, keyer groupFunc) []result {
|
||||||
|
grouped := make(map[string]result, len(results))
|
||||||
|
|
||||||
|
for _, cur := range results {
|
||||||
|
key := keyer(cur)
|
||||||
|
grouped[key] = cur.add(grouped[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
out := results[:0]
|
||||||
|
for _, r := range grouped {
|
||||||
|
out = append(out, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
21
27-functional-programming/log-parser-exp/groupers.go
Normal file
21
27-functional-programming/log-parser-exp/groupers.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// domainGrouper groups by domain.
|
||||||
|
// but it keeps the other fields.
|
||||||
|
// for example: it returns pages as well, but you shouldn't use them.
|
||||||
|
// exercise: write a function that erases superfluous data.
|
||||||
|
func domainGrouper(r result) string {
|
||||||
|
return r.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageGrouper(r result) string {
|
||||||
|
return r.domain + r.page
|
||||||
|
}
|
||||||
|
|
||||||
|
// you could have created a noopGrouper as well
|
||||||
|
// but it's not necessary i think (map allocation)
|
||||||
|
func noopGrouper(r result) string {
|
||||||
|
// with something like:
|
||||||
|
// return randomStrings()
|
||||||
|
return ""
|
||||||
|
}
|
16
27-functional-programming/log-parser-exp/log.txt
Normal file
16
27-functional-programming/log-parser-exp/log.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
learngoprogramming.com.tr / 10 5
|
||||||
|
learngoprogramming.com /courses 15 10
|
||||||
|
learngoprogramming.com /courses 10 5
|
||||||
|
learngoprogramming.com /articles 20 15
|
||||||
|
learngoprogramming.com /articles 5 2
|
||||||
|
golang.org / 40 20
|
||||||
|
golang.org / 20 10
|
||||||
|
golang.org /blog 45 25
|
||||||
|
golang.org /blog 15 5
|
||||||
|
blog.golang.org /courses 60 30
|
||||||
|
blog.golang.org /courses 30 20
|
||||||
|
blog.golang.org /updates 20 10
|
||||||
|
blog.golang.org /reference 65 35
|
||||||
|
blog.golang.org /reference 15 5
|
||||||
|
inanc.io /about 30 15
|
||||||
|
inanc.io /about 70 35
|
16
27-functional-programming/log-parser-exp/log_err_missing.txt
Normal file
16
27-functional-programming/log-parser-exp/log_err_missing.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
learngoprogramming.com / 10 5
|
||||||
|
learngoprogramming.com /courses 15 10
|
||||||
|
learngoprogramming.com /courses 10 5
|
||||||
|
learngoprogramming.com /articles 20 15
|
||||||
|
learngoprogramming.com /articles 5 2
|
||||||
|
golang.org / 40 20
|
||||||
|
golang.org / 20 10
|
||||||
|
golang.org /blog 45 25
|
||||||
|
golang.org /blog 15 5
|
||||||
|
blog.golang.org /updates
|
||||||
|
blog.golang.org /updates 30 20
|
||||||
|
blog.golang.org /updates 20 10
|
||||||
|
blog.golang.org /reference 65 35
|
||||||
|
blog.golang.org /reference 15 5
|
||||||
|
inanc.io /about 30 15
|
||||||
|
inanc.io /about 70 35
|
@ -0,0 +1,16 @@
|
|||||||
|
learngoprogramming.com / 10 5
|
||||||
|
learngoprogramming.com /courses 15 10
|
||||||
|
learngoprogramming.com /courses 10 5
|
||||||
|
learngoprogramming.com /articles 20 15
|
||||||
|
learngoprogramming.com /articles 5 2
|
||||||
|
golang.org / 40 20
|
||||||
|
golang.org / 20 10
|
||||||
|
golang.org /blog 45 -250
|
||||||
|
golang.org /blog 15 5
|
||||||
|
blog.golang.org /updates 60 30
|
||||||
|
blog.golang.org /updates 30 20
|
||||||
|
blog.golang.org /updates 20 10
|
||||||
|
blog.golang.org /reference 65 35
|
||||||
|
blog.golang.org /reference 15 5
|
||||||
|
inanc.io /about 30 15
|
||||||
|
inanc.io /about 70 35
|
16
27-functional-programming/log-parser-exp/log_err_str.txt
Normal file
16
27-functional-programming/log-parser-exp/log_err_str.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
learngoprogramming.com / 10 5
|
||||||
|
learngoprogramming.com /courses 15 10
|
||||||
|
learngoprogramming.com /courses 10 5
|
||||||
|
learngoprogramming.com /articles 20 15
|
||||||
|
learngoprogramming.com /articles 5 2
|
||||||
|
golang.org / 40 TWENTY
|
||||||
|
golang.org / 20 10
|
||||||
|
golang.org /blog 45 25
|
||||||
|
golang.org /blog 15 5
|
||||||
|
blog.golang.org /updates 60 30
|
||||||
|
blog.golang.org /updates 30 20
|
||||||
|
blog.golang.org /updates 20 10
|
||||||
|
blog.golang.org /reference 65 35
|
||||||
|
blog.golang.org /reference 15 5
|
||||||
|
inanc.io /about 30 15
|
||||||
|
inanc.io /about 70 35
|
49
27-functional-programming/log-parser-exp/main.go
Normal file
49
27-functional-programming/log-parser-exp/main.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// 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"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer recoverErr()
|
||||||
|
|
||||||
|
_, err := newReport().
|
||||||
|
from(os.Stdin).
|
||||||
|
to(os.Stdout).
|
||||||
|
retrieveFrom(textReader).
|
||||||
|
filterBy(orgDomainsFilter).
|
||||||
|
// filterBy(not(domainExtFilter("org", "io"))).
|
||||||
|
// groupBy(pageGrouper).
|
||||||
|
groupBy(domainGrouper).
|
||||||
|
writeTo(textWriter).
|
||||||
|
run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("> Err:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recoverErr() {
|
||||||
|
val := recover()
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := val.(string); ok {
|
||||||
|
fmt.Println("> Error occurred:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
newReport -> stats.NewReport().
|
||||||
|
Result -> stats.Record
|
||||||
|
*/
|
85
27-functional-programming/log-parser-exp/report.go
Normal file
85
27-functional-programming/log-parser-exp/report.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type (
|
||||||
|
parserFunc func(io.Reader) ([]result, error)
|
||||||
|
filterFunc func(result) bool
|
||||||
|
groupFunc func(result) string
|
||||||
|
outputFunc func(io.Writer, []result) error
|
||||||
|
)
|
||||||
|
|
||||||
|
type report struct {
|
||||||
|
input io.Reader
|
||||||
|
output io.Writer
|
||||||
|
parser parserFunc
|
||||||
|
filterer filterFunc
|
||||||
|
grouper groupFunc
|
||||||
|
outputter outputFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReport() *report {
|
||||||
|
return &report{
|
||||||
|
// parser: textParser,
|
||||||
|
filterer: noopFilter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) from(reader io.Reader) *report {
|
||||||
|
r.input = reader
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) to(writer io.Writer) *report {
|
||||||
|
r.output = writer
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) retrieveFrom(fn parserFunc) *report {
|
||||||
|
r.parser = fn
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) filterBy(fn filterFunc) *report {
|
||||||
|
r.filterer = fn
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) groupBy(fn groupFunc) *report {
|
||||||
|
r.grouper = fn
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) writeTo(fn outputFunc) *report {
|
||||||
|
r.outputter = fn
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *report) run() ([]result, error) {
|
||||||
|
if r.parser == nil {
|
||||||
|
panic("report retriever cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := r.parser(r.input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// noop if filterer is nil
|
||||||
|
results = filterBy(results, r.filterer)
|
||||||
|
|
||||||
|
// grouper is more tricky
|
||||||
|
// you don't want to create an unnecessary map
|
||||||
|
if r.grouper != nil {
|
||||||
|
results = groupBy(results, r.grouper)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer: noop output
|
||||||
|
if r.output != nil {
|
||||||
|
if err := r.outputter(r.output, results); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
38
27-functional-programming/log-parser-exp/result.go
Normal file
38
27-functional-programming/log-parser-exp/result.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// result stores the parsed result for a domain
|
||||||
|
type result struct {
|
||||||
|
domain string
|
||||||
|
page string
|
||||||
|
visits int
|
||||||
|
uniques int
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLine parses a log line and returns the parsed result with an error
|
||||||
|
func parseLine(line string) (r result, err error) {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) != 4 {
|
||||||
|
return r, fmt.Errorf("wrong number of fields -> %v", fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.domain = fields[0]
|
||||||
|
r.page = fields[1]
|
||||||
|
|
||||||
|
f := new(field)
|
||||||
|
r.visits = f.uatoi("visits", fields[2])
|
||||||
|
r.uniques = f.uatoi("uniques", fields[3])
|
||||||
|
|
||||||
|
return r, f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add adds the metrics of another result to the result
|
||||||
|
func (r result) add(other result) result {
|
||||||
|
r.visits += other.visits
|
||||||
|
r.uniques += other.uniques
|
||||||
|
return r
|
||||||
|
}
|
44
27-functional-programming/log-parser-exp/textreader.go
Normal file
44
27-functional-programming/log-parser-exp/textreader.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func textReader(r io.Reader) ([]result, error) {
|
||||||
|
in := bufio.NewScanner(r)
|
||||||
|
return parseText(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseText(in *bufio.Scanner) ([]result, error) {
|
||||||
|
var (
|
||||||
|
results []result
|
||||||
|
lines int
|
||||||
|
)
|
||||||
|
|
||||||
|
for in.Scan() {
|
||||||
|
lines++
|
||||||
|
|
||||||
|
result, err := parseLine(in.Text())
|
||||||
|
if err != nil {
|
||||||
|
// TODO: custom error type for line information
|
||||||
|
return nil, fmt.Errorf("line %d: %v", lines, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := in.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
30
27-functional-programming/log-parser-exp/textwriter.go
Normal file
30
27-functional-programming/log-parser-exp/textwriter.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: sort by result key interfaces section
|
||||||
|
|
||||||
|
func textWriter(w io.Writer, results []result) error {
|
||||||
|
fmt.Fprintf(w, "%-25s %-10s %10s %10s\n",
|
||||||
|
"DOMAINS", "PAGES", "VISITS", "UNIQUES")
|
||||||
|
|
||||||
|
fmt.Fprintln(w, strings.Repeat("-", 58))
|
||||||
|
|
||||||
|
var total result
|
||||||
|
|
||||||
|
for _, r := range results {
|
||||||
|
total = total.add(r)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%-25s %-10s %10d %10d\n",
|
||||||
|
r.domain, r.page, r.visits, r.uniques)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "\n%-36s %10d %10d\n",
|
||||||
|
"", total.visits, total.uniques)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
learngoprogramming.com 10
|
|
||||||
learngoprogramming.com 10
|
|
||||||
golang.org 4
|
|
||||||
golang.org 6
|
|
||||||
blog.golang.org 20
|
|
||||||
blog.golang.org 10
|
|
@ -1,6 +0,0 @@
|
|||||||
learngoprogramming.com 10
|
|
||||||
learngoprogramming.com 10
|
|
||||||
golang.org
|
|
||||||
golang.org 6
|
|
||||||
blog.golang.org 20
|
|
||||||
blog.golang.org 10
|
|
@ -1,6 +0,0 @@
|
|||||||
learngoprogramming.com 10
|
|
||||||
learngoprogramming.com 10
|
|
||||||
golang.org -100
|
|
||||||
golang.org 6
|
|
||||||
blog.golang.org 20
|
|
||||||
blog.golang.org 10
|
|
@ -1,6 +0,0 @@
|
|||||||
learngoprogramming.com 10
|
|
||||||
learngoprogramming.com 10
|
|
||||||
golang.org FOUR
|
|
||||||
golang.org 6
|
|
||||||
blog.golang.org 20
|
|
||||||
blog.golang.org 10
|
|
@ -1,48 +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"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
p := newParser()
|
|
||||||
|
|
||||||
in := bufio.NewScanner(os.Stdin)
|
|
||||||
for in.Scan() {
|
|
||||||
parsed := parse(p, in.Text())
|
|
||||||
update(p, parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
summarize(p)
|
|
||||||
dumpErrs(in.Err(), err(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
// summarize summarizes and prints the parsing result
|
|
||||||
func summarize(p *parser) {
|
|
||||||
fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS")
|
|
||||||
fmt.Println(strings.Repeat("-", 45))
|
|
||||||
|
|
||||||
loop(p, func(r result) {
|
|
||||||
fmt.Printf("%-30s %10d\n", r.domain, r.visits)
|
|
||||||
})
|
|
||||||
fmt.Printf("\n%-30s %10d\n", "TOTAL", totalVisits(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
// dumpErrs simplifies handling multiple errors
|
|
||||||
func dumpErrs(errs ...error) {
|
|
||||||
for _, err := range errs {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("> Err:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,101 +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 (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"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) (r 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
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
r.domain = fields[0]
|
|
||||||
r.visits, err = strconv.Atoi(fields[1])
|
|
||||||
|
|
||||||
if r.visits < 0 || err != nil {
|
|
||||||
p.lerr = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// update updates all the parsing results using the given parsing result
|
|
||||||
func update(p *parser, r result) {
|
|
||||||
if p.lerr != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect the unique domains
|
|
||||||
if _, ok := p.sum[r.domain]; !ok {
|
|
||||||
p.domains = append(p.domains, r.domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of total and per domain visits
|
|
||||||
p.total += r.visits
|
|
||||||
|
|
||||||
// create and assign a new copy of `visit`
|
|
||||||
p.sum[r.domain] = result{
|
|
||||||
domain: r.domain,
|
|
||||||
visits: r.visits + p.sum[r.domain].visits,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// err returns the last error encountered
|
|
||||||
func err(p *parser) error {
|
|
||||||
return p.lerr
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop allows a func to access all the copies of parsing results
|
|
||||||
func loop(p *parser, feed func(result)) {
|
|
||||||
sort.Strings(p.domains)
|
|
||||||
|
|
||||||
for _, domain := range p.domains {
|
|
||||||
feed(p.sum[domain])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// total returns the total visits
|
|
||||||
func totalVisits(p *parser) int {
|
|
||||||
return p.total
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
# IDEAS
|
|
||||||
|
|
||||||
* Create a chainable funcs for strings
|
|
||||||
* Create a mapper instead of a filter
|
|
||||||
* Create map/reduce
|
|
||||||
* Create a time it takes higher-order func
|
|
||||||
* Use stdlib funcs with func values
|
|
||||||
* Create a functional program with structs (filter etc)
|
|
Reference in New Issue
Block a user