go-02-word-frequency
0.500
Challenge · difficulty 2/5
# Word frequency
Implement **`solution.go`** in `package challenge` exporting:
```go
func WordFrequency(text string) map[string]int
```
Count how many times each word occurs in `text` and return the counts in a map.
Rules:
- Split `text` into tokens on **whitespace** (spaces, tabs, newlines).
- For each token, strip any **surrounding ASCII punctuation** (leading and trailing).
Punctuation in the middle of a token is kept.
- **Lowercase** each word before counting.
- If, after stripping, a token is empty, skip it (do not count an empty string).
- For empty or whitespace-only input, return an **empty, non-nil** map (length 0).
ASCII punctuation is the set of characters for which Go's `unicode.IsPunct` returns true
together with symbols such as `+`, `<`, `=`, etc. For this challenge, treat a byte as
"punctuation to strip" when it is an ASCII byte that is **not** a letter or digit.
Examples:
- `WordFrequency("the cat sat on the mat")` →
`{"the": 2, "cat": 1, "sat": 1, "on": 1, "mat": 1}`
- `WordFrequency("Hello, hello! HELLO.")` → `{"hello": 3}`
- `WordFrequency("don't stop")` → `{"don't": 1, "stop": 1}` (interior apostrophe kept)
- `WordFrequency(" ")` → `{}` (empty, non-nil map)
tests/solution_test.go
package challenge
import (
"reflect"
"testing"
)
func TestWordFrequency(t *testing.T) {
cases := []struct {
name string
in string
want map[string]int
}{
{
name: "simple repeats",
in: "the cat sat on the mat",
want: map[string]int{"the": 2, "cat": 1, "sat": 1, "on": 1, "mat": 1},
},
{
name: "punctuation and case",
in: "Hello, hello! HELLO.",
want: map[string]int{"hello": 3},
},
{
name: "interior apostrophe kept",
in: "don't stop don't",
want: map[string]int{"don't": 2, "stop": 1},
},
{
name: "tabs and newlines as whitespace",
in: "a\tb\nc a",
want: map[string]int{"a": 2, "b": 1, "c": 1},
},
{
name: "leading and trailing punctuation stripped",
in: "(go) [go]; {GO}",
want: map[string]int{"go": 3},
},
{
name: "digits are words",
in: "42 42 forty-two",
want: map[string]int{"42": 2, "forty-two": 1},
},
{
name: "token that is only punctuation is skipped",
in: "hi --- !!! bye",
want: map[string]int{"hi": 1, "bye": 1},
},
{
name: "empty input",
in: "",
want: map[string]int{},
},
{
name: "whitespace only",
in: " \t \n ",
want: map[string]int{},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := WordFrequency(c.in)
if got == nil {
t.Fatalf("WordFrequency(%q) returned nil map, want non-nil", c.in)
}
if !reflect.DeepEqual(got, c.want) {
t.Errorf("WordFrequency(%q) = %v, want %v", c.in, got, c.want)
}
})
}
}
func TestWordFrequencyEmptyIsNonNil(t *testing.T) {
got := WordFrequency("")
if got == nil {
t.Fatal("WordFrequency(\"\") = nil, want non-nil empty map")
}
if len(got) != 0 {
t.Errorf("WordFrequency(\"\") = %v, want empty map", got)
}
}
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.Proposed solution
```go
package challenge
import (
"strings"
"unicode"
)
func WordFrequency(text string) map[string]int {
wordCounts := make(map[string]int)
if len(text) == 0 {
return wordCounts
}
words := strings.FieldsFunc(text, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
})
for _, word := range words {
word = strings.ToLower(word)
word = strings.TrimFunc(word, func(r rune) bool {
return unicode.IsPunct(r) || !unicode.IsLetter(r) && !unicode.IsNumber(r)
})
if len(word) == 0 {
continue
}
wordCounts[word]++
}
return wordCounts
}
```Test output (stdout)
digits_are_words","Output":"=== RUN TestWordFrequency/digits_are_words\n"}
{"Time":"2026-07-03T03:07:50.841422114+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/digits_are_words","Output":" solution_test.go:68: WordFrequency(\"42 42 forty-two\") = map[42:2 forty:1 two:1], want map[42:2 forty-two:1]\n"}
{"Time":"2026-07-03T03:07:50.841424024+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/digits_are_words","Output":"--- FAIL: TestWordFrequency/digits_are_words (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841425504+02:00","Action":"fail","Package":"challenge","Test":"TestWordFrequency/digits_are_words","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841427004+02:00","Action":"run","Package":"challenge","Test":"TestWordFrequency/token_that_is_only_punctuation_is_skipped"}
{"Time":"2026-07-03T03:07:50.841428384+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/token_that_is_only_punctuation_is_skipped","Output":"=== RUN TestWordFrequency/token_that_is_only_punctuation_is_skipped\n"}
{"Time":"2026-07-03T03:07:50.841430124+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/token_that_is_only_punctuation_is_skipped","Output":"--- PASS: TestWordFrequency/token_that_is_only_punctuation_is_skipped (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841431744+02:00","Action":"pass","Package":"challenge","Test":"TestWordFrequency/token_that_is_only_punctuation_is_skipped","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841435374+02:00","Action":"run","Package":"challenge","Test":"TestWordFrequency/empty_input"}
{"Time":"2026-07-03T03:07:50.841436784+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/empty_input","Output":"=== RUN TestWordFrequency/empty_input\n"}
{"Time":"2026-07-03T03:07:50.841439074+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/empty_input","Output":"--- PASS: TestWordFrequency/empty_input (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841441014+02:00","Action":"pass","Package":"challenge","Test":"TestWordFrequency/empty_input","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841442444+02:00","Action":"run","Package":"challenge","Test":"TestWordFrequency/whitespace_only"}
{"Time":"2026-07-03T03:07:50.841444594+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/whitespace_only","Output":"=== RUN TestWordFrequency/whitespace_only\n"}
{"Time":"2026-07-03T03:07:50.841446354+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency/whitespace_only","Output":"--- PASS: TestWordFrequency/whitespace_only (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841447724+02:00","Action":"pass","Package":"challenge","Test":"TestWordFrequency/whitespace_only","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841449524+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequency","Output":"--- FAIL: TestWordFrequency (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841450944+02:00","Action":"fail","Package":"challenge","Test":"TestWordFrequency","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841452684+02:00","Action":"run","Package":"challenge","Test":"TestWordFrequencyEmptyIsNonNil"}
{"Time":"2026-07-03T03:07:50.841453844+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequencyEmptyIsNonNil","Output":"=== RUN TestWordFrequencyEmptyIsNonNil\n"}
{"Time":"2026-07-03T03:07:50.841460664+02:00","Action":"output","Package":"challenge","Test":"TestWordFrequencyEmptyIsNonNil","Output":"--- PASS: TestWordFrequencyEmptyIsNonNil (0.00s)\n"}
{"Time":"2026-07-03T03:07:50.841461984+02:00","Action":"pass","Package":"challenge","Test":"TestWordFrequencyEmptyIsNonNil","Elapsed":0}
{"Time":"2026-07-03T03:07:50.841462994+02:00","Action":"output","Package":"challenge","Output":"FAIL\n"}
{"Time":"2026-07-03T03:07:50.841641835+02:00","Action":"output","Package":"challenge","Output":"FAIL\tchallenge\t0.001s\n"}
{"Time":"2026-07-03T03:07:50.841651835+02:00","Action":"fail","Package":"challenge","Elapsed":0.001}
Self-repair attempts
[
{
"total": 2,
"answer": "```go\npackage challenge\n\nimport (\n\t\"strings\"\n\t\"unicode\"\n)\n\nfunc WordFrequency(text string) map[string]int {\n\twordCounts := make(map[string]int)\n\n\tif len(text) == 0 {\n\t\treturn wordCounts\n\t}\n\n\twords := strings.FieldsFunc(text, func(r rune) bool {\n\t\treturn !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t})\n\n\tfor _, word := range words {\n\t\tword = strings.ToLower(word)\n\t\tword = strings.TrimFunc(word, func(r rune) bool {\n\t\t\treturn unicode.IsPunct(r) || !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t\t})\n\n\t\tif len(word) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\twordCounts[word]++\n\t}\n\n\treturn wordCounts\n}\n```",
"passed": 1,
"test_error": "3:07:50.841435374+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\"}\n{\"Time\":\"2026-07-03T03:07:50.841436784+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Output\":\"=== RUN TestWordFrequency/empty_input\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841439074+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Output\":\"--- PASS: TestWordFrequency/empty_input (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841441014+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:50.841442444+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\"}\n{\"Time\":\"2026-07-03T03:07:50.841444594+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Output\":\"=== RUN TestWordFrequency/whitespace_only\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841446354+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Output\":\"--- PASS: TestWordFrequency/whitespace_only (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841447724+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:50.841449524+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency\",\"Output\":\"--- FAIL: TestWordFrequency (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841450944+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:50.841452684+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\"}\n{\"Time\":\"2026-07-03T03:07:50.841453844+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Output\":\"=== RUN TestWordFrequencyEmptyIsNonNil\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841460664+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Output\":\"--- PASS: TestWordFrequencyEmptyIsNonNil (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841461984+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:50.841462994+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841641835+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\tchallenge\\t0.001s\\n\"}\n{\"Time\":\"2026-07-03T03:07:50.841651835+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Elapsed\":0.001}"
},
{
"total": 2,
"answer": "```go\npackage challenge\n\nimport (\n\t\"strings\"\n\t\"unicode\"\n)\n\nfunc WordFrequency(text string) map[string]int {\n\twordCounts := make(map[string]int)\n\n\tif len(text) == 0 {\n\t\treturn wordCounts\n\t}\n\n\twords := strings.FieldsFunc(text, func(r rune) bool {\n\t\treturn !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t})\n\n\tfor _, word := range words {\n\t\tword = strings.ToLower(word)\n\t\tword = strings.TrimFunc(word, func(r rune) bool {\n\t\t\treturn unicode.IsPunct(r) || !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t\t})\n\n\t\tif len(word) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\twordCounts[word]++\n\t}\n\n\treturn wordCounts\n}\n```",
"passed": 1,
"test_error": "T03:07:53.448781659+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\"}\n{\"Time\":\"2026-07-03T03:07:53.448782629+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Output\":\"=== RUN TestWordFrequency/empty_input\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448783699+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Output\":\"--- PASS: TestWordFrequency/empty_input (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448784589+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/empty_input\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:53.448785419+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\"}\n{\"Time\":\"2026-07-03T03:07:53.448786309+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Output\":\"=== RUN TestWordFrequency/whitespace_only\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448787459+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Output\":\"--- PASS: TestWordFrequency/whitespace_only (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448789129+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency/whitespace_only\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:53.448790139+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency\",\"Output\":\"--- FAIL: TestWordFrequency (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448791869+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequency\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:53.448792889+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\"}\n{\"Time\":\"2026-07-03T03:07:53.448793749+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Output\":\"=== RUN TestWordFrequencyEmptyIsNonNil\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448795089+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Output\":\"--- PASS: TestWordFrequencyEmptyIsNonNil (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.448796059+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestWordFrequencyEmptyIsNonNil\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T03:07:53.448796989+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.44897376+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\tchallenge\\t0.001s\\n\"}\n{\"Time\":\"2026-07-03T03:07:53.44898483+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Elapsed\":0.001}"
},
{
"total": 2,
"answer": "```go\npackage challenge\n\nimport (\n\t\"strings\"\n\t\"unicode\"\n)\n\nfunc WordFrequency(text string) map[string]int {\n\twordCounts := make(map[string]int)\n\n\tif len(text) == 0 {\n\t\treturn wordCounts\n\t}\n\n\twords := strings.FieldsFunc(text, func(r rune) bool {\n\t\treturn !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t})\n\n\tfor _, word := range words {\n\t\tword = strings.ToLower(word)\n\t\tword = strings.TrimFunc(word, func(r rune) bool {\n\t\t\treturn unicode.IsPunct(r) || !unicode.IsLetter(r) && !unicode.IsNumber(r)\n\t\t})\n\n\t\tif len(word) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\twordCounts[word]++\n\t}\n\n\treturn wordCounts\n}\n```",
"passed": 1,
"test_error": ""
}
]