From 7a6b109e789a37041ae2e98bff6ab925fcb1d9c8 Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Thu, 14 May 2026 11:24:48 +0300 Subject: [PATCH] init --- cmd/namecount/main.go | 32 ++++++++++++++ go.mod | 3 ++ internal/counter/counter.go | 53 ++++++++++++++++++++++ internal/counter/counter_test.go | 76 ++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 cmd/namecount/main.go create mode 100644 go.mod create mode 100644 internal/counter/counter.go create mode 100644 internal/counter/counter_test.go diff --git a/cmd/namecount/main.go b/cmd/namecount/main.go new file mode 100644 index 0000000..23f75ad --- /dev/null +++ b/cmd/namecount/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "os" + + "tallin/internal/counter" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "usage: %s \n", os.Args[0]) + os.Exit(1) + } + + file, err := os.Open(os.Args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "open file: %v\n", err) + os.Exit(1) + } + defer file.Close() + + counts, err := counter.Count(file) + if err != nil { + fmt.Fprintf(os.Stderr, "read file: %v\n", err) + os.Exit(1) + } + + for _, result := range counter.Sorted(counts) { + fmt.Printf("%s:%d\n", result.Name, result.Count) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..eac0259 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module tallin + +go 1.22 diff --git a/internal/counter/counter.go b/internal/counter/counter.go new file mode 100644 index 0000000..18e96d0 --- /dev/null +++ b/internal/counter/counter.go @@ -0,0 +1,53 @@ +package counter + +import ( + "bufio" + "io" + "sort" +) + +const maxLineSize = 1024 * 1024 + +type Result struct { + Name string + Count int +} + +func Count(r io.Reader) (map[string]int, error) { + counts := make(map[string]int) + + scanner := bufio.NewScanner(r) + scanner.Buffer(make([]byte, 64*1024), maxLineSize) + + for scanner.Scan() { + name := scanner.Text() + if name == "" { + continue + } + + counts[name]++ + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return counts, nil +} + +func Sorted(counts map[string]int) []Result { + results := make([]Result, 0, len(counts)) + for name, count := range counts { + results = append(results, Result{Name: name, Count: count}) + } + + sort.Slice(results, func(i, j int) bool { + if results[i].Count != results[j].Count { + return results[i].Count > results[j].Count + } + + return results[i].Name < results[j].Name + }) + + return results +} diff --git a/internal/counter/counter_test.go b/internal/counter/counter_test.go new file mode 100644 index 0000000..000b111 --- /dev/null +++ b/internal/counter/counter_test.go @@ -0,0 +1,76 @@ +package counter + +import ( + "reflect" + "strings" + "testing" +) + +func TestCount(t *testing.T) { + t.Parallel() + + counts, err := Count(strings.NewReader("Алена\nМиша\nАлена\nДима\n\n")) + if err != nil { + t.Fatalf("Count() error = %v", err) + } + + want := map[string]int{ + "Алена": 2, + "Миша": 1, + "Дима": 1, + } + if !reflect.DeepEqual(counts, want) { + t.Fatalf("Count() = %#v, want %#v", counts, want) + } +} + +func TestCountPreservesSpaces(t *testing.T) { + t.Parallel() + + counts, err := Count(strings.NewReader("Anna\n Anna \nAnna\n")) + if err != nil { + t.Fatalf("Count() error = %v", err) + } + + want := map[string]int{ + "Anna": 2, + " Anna ": 1, + } + if !reflect.DeepEqual(counts, want) { + t.Fatalf("Count() = %#v, want %#v", counts, want) + } +} + +func TestCountEmptyInput(t *testing.T) { + t.Parallel() + + counts, err := Count(strings.NewReader("\n\n")) + if err != nil { + t.Fatalf("Count() error = %v", err) + } + + if len(counts) != 0 { + t.Fatalf("Count() = %#v, want empty map", counts) + } +} + +func TestSorted(t *testing.T) { + t.Parallel() + + counts := map[string]int{ + "Миша": 1, + "Алена": 2, + "Дима": 1, + } + + got := Sorted(counts) + want := []Result{ + {Name: "Алена", Count: 2}, + {Name: "Дима", Count: 1}, + {Name: "Миша", Count: 1}, + } + + if !reflect.DeepEqual(got, want) { + t.Fatalf("Sorted() = %#v, want %#v", got, want) + } +}