go-04-map-concurrent
0.600
Challenge · difficulty 4/5
# Concurrent ordered map
Implement **`solution.go`** in `package challenge` exporting:
```go
func MapConcurrent(inputs []int, workers int, fn func(int) int) []int
```
Apply `fn` to every element of `inputs` using a pool of **at most `workers`**
goroutines running concurrently, and return the results.
Rules:
- The returned slice has the **same length** as `inputs`, and `result[i]` is
`fn(inputs[i])` — i.e. results are in the **same order as the inputs**,
regardless of the order goroutines finish in.
- Work must be distributed across concurrent goroutines (use goroutines plus
channels and/or `sync.WaitGroup`). No more than `workers` goroutines may be
processing elements at the same time.
- `workers` may be larger than `len(inputs)`; never start more workers than
there is work for, and never start fewer than one when there is work.
- If `inputs` is empty (or `nil`), return an empty (non-nil is fine, but it must
have length 0) slice without starting any work.
- You may assume `workers >= 1` and that `fn` is safe to call concurrently
(it does not share mutable state).
Examples:
- `MapConcurrent([]int{1, 2, 3}, 2, func(x int) int { return x * x })` → `[]int{1, 4, 9}`
- `MapConcurrent([]int{}, 4, fn)` → `[]int{}`
- `MapConcurrent([]int{5}, 8, func(x int) int { return x + 1 })` → `[]int{6}`
tests/solution_test.go
package challenge
import (
"reflect"
"sync"
"sync/atomic"
"testing"
)
func TestMapConcurrentMatchesSequential(t *testing.T) {
fn := func(x int) int { return x*x + 1 }
cases := []struct {
name string
inputs []int
workers int
}{
{"empty", []int{}, 4},
{"nil", nil, 4},
{"single", []int{7}, 1},
{"single many workers", []int{7}, 16},
{"workers one", []int{1, 2, 3, 4, 5}, 1},
{"workers equal len", []int{1, 2, 3, 4}, 4},
{"workers gt len", []int{1, 2, 3}, 10},
{"larger", []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2}, 3},
{"negatives", []int{-5, -4, -3, -2, -1}, 2},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
want := make([]int, len(c.inputs))
for i, v := range c.inputs {
want[i] = fn(v)
}
got := MapConcurrent(c.inputs, c.workers, fn)
if len(got) != len(c.inputs) {
t.Fatalf("len = %d, want %d", len(got), len(c.inputs))
}
if !reflect.DeepEqual(got, want) {
t.Errorf("MapConcurrent(%v, %d) = %v, want %v", c.inputs, c.workers, got, want)
}
})
}
}
func TestMapConcurrentOrderPreserved(t *testing.T) {
// Identity maps input value to output position; any reordering would be caught.
inputs := make([]int, 200)
for i := range inputs {
inputs[i] = i * 3
}
got := MapConcurrent(inputs, 7, func(x int) int { return x })
for i, v := range inputs {
if got[i] != v {
t.Fatalf("index %d = %d, want %d (order not preserved)", i, got[i], v)
}
}
}
func TestMapConcurrentEmptyReturnsLenZero(t *testing.T) {
got := MapConcurrent(nil, 4, func(x int) int { return x })
if len(got) != 0 {
t.Fatalf("len = %d, want 0", len(got))
}
}
func TestMapConcurrentRespectsWorkerLimit(t *testing.T) {
inputs := make([]int, 100)
for i := range inputs {
inputs[i] = i
}
const limit = 4
var mu sync.Mutex
cond := sync.NewCond(&mu)
var active int64
var maxActive int64
var arrived int
// fn blocks until either `limit` callers are concurrently in flight or no more
// callers can arrive. This forces real concurrency while bounding it at `limit`:
// if the implementation ran fewer than `limit` workers it would deadlock-stall,
// and any run above `limit` would be recorded in maxActive.
fn := func(x int) int {
cur := atomic.AddInt64(&active, 1)
for {
m := atomic.LoadInt64(&maxActive)
if cur <= m || atomic.CompareAndSwapInt64(&maxActive, m, cur) {
break
}
}
mu.Lock()
arrived++
if arrived >= limit {
cond.Broadcast()
}
for arrived < limit {
cond.Wait()
}
mu.Unlock()
atomic.AddInt64(&active, -1)
return x * 2
}
got := MapConcurrent(inputs, limit, fn)
if m := atomic.LoadInt64(&maxActive); m > limit {
t.Fatalf("observed %d concurrent workers, limit was %d", m, limit)
}
if atomic.LoadInt64(&maxActive) < 2 {
t.Fatalf("no real concurrency observed (maxActive=%d)", maxActive)
}
for i := range inputs {
if got[i] != inputs[i]*2 {
t.Fatalf("index %d = %d, want %d", i, got[i], inputs[i]*2)
}
}
}
func TestMapConcurrentCallsFnOncePerElement(t *testing.T) {
inputs := []int{10, 20, 30, 40, 50, 60}
var calls int64
got := MapConcurrent(inputs, 3, func(x int) int {
atomic.AddInt64(&calls, 1)
return x + 1
})
if calls != int64(len(inputs)) {
t.Fatalf("fn called %d times, want %d", calls, len(inputs))
}
for i, v := range inputs {
if got[i] != v+1 {
t.Errorf("index %d = %d, want %d", i, got[i], v+1)
}
}
}
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
func MapConcurrent(inputs []int, workers int, fn func(int) int) []int {
if len(inputs) == 0 {
return []int{}
}
// Ensure we don't create more workers than needed
workers = min(workers, len(inputs))
// Create channels for tasks and results
tasks := make(chan int, len(inputs))
results := make(chan int, len(inputs))
// Start worker pool
for i := 0; i < workers; i++ {
go func() {
for x := range tasks {
results <- fn(x)
}
}()
}
// Send all tasks to the worker pool
for _, x := range inputs {
tasks <- x
}
close(tasks)
// Collect results in order
output := make([]int, len(inputs))
for i := 0; i < len(inputs); i++ {
output[i] = <-results
}
return output
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
```Test output (stdout)
Test":"TestMapConcurrentMatchesSequential/negatives","Output":"=== RUN TestMapConcurrentMatchesSequential/negatives\n"}
{"Time":"2026-07-03T00:56:47.519038314+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentMatchesSequential/negatives","Output":"--- PASS: TestMapConcurrentMatchesSequential/negatives (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519040144+02:00","Action":"pass","Package":"challenge","Test":"TestMapConcurrentMatchesSequential/negatives","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519041834+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentMatchesSequential","Output":"--- FAIL: TestMapConcurrentMatchesSequential (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519043134+02:00","Action":"fail","Package":"challenge","Test":"TestMapConcurrentMatchesSequential","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519044054+02:00","Action":"run","Package":"challenge","Test":"TestMapConcurrentOrderPreserved"}
{"Time":"2026-07-03T00:56:47.519044954+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentOrderPreserved","Output":"=== RUN TestMapConcurrentOrderPreserved\n"}
{"Time":"2026-07-03T00:56:47.519061034+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentOrderPreserved","Output":"--- PASS: TestMapConcurrentOrderPreserved (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519062474+02:00","Action":"pass","Package":"challenge","Test":"TestMapConcurrentOrderPreserved","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519063574+02:00","Action":"run","Package":"challenge","Test":"TestMapConcurrentEmptyReturnsLenZero"}
{"Time":"2026-07-03T00:56:47.519064354+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentEmptyReturnsLenZero","Output":"=== RUN TestMapConcurrentEmptyReturnsLenZero\n"}
{"Time":"2026-07-03T00:56:47.519071714+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentEmptyReturnsLenZero","Output":"--- PASS: TestMapConcurrentEmptyReturnsLenZero (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519072854+02:00","Action":"pass","Package":"challenge","Test":"TestMapConcurrentEmptyReturnsLenZero","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519074194+02:00","Action":"run","Package":"challenge","Test":"TestMapConcurrentRespectsWorkerLimit"}
{"Time":"2026-07-03T00:56:47.519075054+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentRespectsWorkerLimit","Output":"=== RUN TestMapConcurrentRespectsWorkerLimit\n"}
{"Time":"2026-07-03T00:56:47.519134894+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentRespectsWorkerLimit","Output":" solution_test.go:112: index 0 = 6, want 0\n"}
{"Time":"2026-07-03T00:56:47.519148574+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentRespectsWorkerLimit","Output":"--- FAIL: TestMapConcurrentRespectsWorkerLimit (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519152474+02:00","Action":"fail","Package":"challenge","Test":"TestMapConcurrentRespectsWorkerLimit","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519155244+02:00","Action":"run","Package":"challenge","Test":"TestMapConcurrentCallsFnOncePerElement"}
{"Time":"2026-07-03T00:56:47.519157024+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentCallsFnOncePerElement","Output":"=== RUN TestMapConcurrentCallsFnOncePerElement\n"}
{"Time":"2026-07-03T00:56:47.519159084+02:00","Action":"output","Package":"challenge","Test":"TestMapConcurrentCallsFnOncePerElement","Output":"--- PASS: TestMapConcurrentCallsFnOncePerElement (0.00s)\n"}
{"Time":"2026-07-03T00:56:47.519160374+02:00","Action":"pass","Package":"challenge","Test":"TestMapConcurrentCallsFnOncePerElement","Elapsed":0}
{"Time":"2026-07-03T00:56:47.519162064+02:00","Action":"output","Package":"challenge","Output":"FAIL\n"}
{"Time":"2026-07-03T00:56:47.519258354+02:00","Action":"output","Package":"challenge","Output":"FAIL\tchallenge\t0.001s\n"}
{"Time":"2026-07-03T00:56:47.519266384+02:00","Action":"fail","Package":"challenge","Elapsed":0.001}
Self-repair attempts
[
{
"total": 5,
"answer": "```go\npackage challenge\n\nfunc MapConcurrent(inputs []int, workers int, fn func(int) int) []int {\n\tif len(inputs) == 0 {\n\t\treturn []int{}\n\t}\n\n\t// Ensure we don't create more workers than needed\n\tworkers = min(workers, len(inputs))\n\n\t// Create channels for tasks and results\n\ttasks := make(chan int, len(inputs))\n\tresults := make(chan int, len(inputs))\n\n\t// Start worker pool\n\tfor i := 0; i < workers; i++ {\n\t\tgo func() {\n\t\t\tfor x := range tasks {\n\t\t\t\tresults <- fn(x)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Send all tasks to the worker pool\n\tfor _, x := range inputs {\n\t\ttasks <- x\n\t}\n\tclose(tasks)\n\n\t// Collect results in order\n\toutput := make([]int, len(inputs))\n\tfor i := 0; i < len(inputs); i++ {\n\t\toutput[i] = <-results\n\t}\n\n\treturn output\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n```",
"passed": 3,
"test_error": "3574+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\"}\n{\"Time\":\"2026-07-03T00:56:47.519064354+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Output\":\"=== RUN TestMapConcurrentEmptyReturnsLenZero\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519071714+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Output\":\"--- PASS: TestMapConcurrentEmptyReturnsLenZero (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519072854+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:47.519074194+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\"}\n{\"Time\":\"2026-07-03T00:56:47.519075054+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\"=== RUN TestMapConcurrentRespectsWorkerLimit\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519134894+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\" solution_test.go:112: index 0 = 6, want 0\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519148574+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\"--- FAIL: TestMapConcurrentRespectsWorkerLimit (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519152474+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:47.519155244+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\"}\n{\"Time\":\"2026-07-03T00:56:47.519157024+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Output\":\"=== RUN TestMapConcurrentCallsFnOncePerElement\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519159084+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Output\":\"--- PASS: TestMapConcurrentCallsFnOncePerElement (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519160374+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:47.519162064+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519258354+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\tchallenge\\t0.001s\\n\"}\n{\"Time\":\"2026-07-03T00:56:47.519266384+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Elapsed\":0.001}"
},
{
"total": 5,
"answer": "```go\npackage challenge\n\nfunc MapConcurrent(inputs []int, workers int, fn func(int) int) []int {\n\tif len(inputs) == 0 {\n\t\treturn []int{}\n\t}\n\n\t// Ensure we don't create more workers than needed\n\tworkers = min(workers, len(inputs))\n\n\t// Create channels for tasks and results\n\ttasks := make(chan int, len(inputs))\n\tresults := make(chan int, len(inputs))\n\n\t// Start worker pool\n\tfor i := 0; i < workers; i++ {\n\t\tgo func(workerID int) {\n\t\t\tfor x := range tasks {\n\t\t\t\tresults <- fn(x)\n\t\t\t}\n\t\t}(i)\n\t}\n\n\t// Send all tasks to the worker pool\n\tfor _, x := range inputs {\n\t\ttasks <- x\n\t}\n\tclose(tasks)\n\n\t// Collect results in order\n\toutput := make([]int, len(inputs))\n\tfor i := 0; i < len(inputs); i++ {\n\t\toutput[i] = <-results\n\t}\n\n\treturn output\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n```",
"passed": 4,
"test_error": "8722+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\"}\n{\"Time\":\"2026-07-03T00:56:54.214331012+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Output\":\"=== RUN TestMapConcurrentEmptyReturnsLenZero\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214334542+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Output\":\"--- PASS: TestMapConcurrentEmptyReturnsLenZero (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214336412+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentEmptyReturnsLenZero\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:54.214338392+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\"}\n{\"Time\":\"2026-07-03T00:56:54.214339912+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\"=== RUN TestMapConcurrentRespectsWorkerLimit\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214342302+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\" solution_test.go:112: index 0 = 6, want 0\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214344612+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Output\":\"--- FAIL: TestMapConcurrentRespectsWorkerLimit (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214346713+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentRespectsWorkerLimit\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:54.214348503+02:00\",\"Action\":\"run\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\"}\n{\"Time\":\"2026-07-03T00:56:54.214350153+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Output\":\"=== RUN TestMapConcurrentCallsFnOncePerElement\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214352303+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Output\":\"--- PASS: TestMapConcurrentCallsFnOncePerElement (0.00s)\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214354503+02:00\",\"Action\":\"pass\",\"Package\":\"challenge\",\"Test\":\"TestMapConcurrentCallsFnOncePerElement\",\"Elapsed\":0}\n{\"Time\":\"2026-07-03T00:56:54.214356803+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214398693+02:00\",\"Action\":\"output\",\"Package\":\"challenge\",\"Output\":\"FAIL\\tchallenge\\t0.001s\\n\"}\n{\"Time\":\"2026-07-03T00:56:54.214404933+02:00\",\"Action\":\"fail\",\"Package\":\"challenge\",\"Elapsed\":0.001}"
},
{
"total": 5,
"answer": "```go\npackage challenge\n\nfunc MapConcurrent(inputs []int, workers int, fn func(int) int) []int {\n\tif len(inputs) == 0 {\n\t\treturn []int{}\n\t}\n\n\t// Ensure we don't create more workers than needed\n\tworkers = min(workers, len(inputs))\n\n\t// Create channels for tasks and results\n\ttasks := make(chan int, len(inputs))\n\tresults := make(chan int, len(inputs))\n\n\t// Start worker pool\n\tfor i := 0; i < workers; i++ {\n\t\tgo func() {\n\t\t\tfor x := range tasks {\n\t\t\t\tresults <- fn(x)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Send all tasks to the worker pool\n\tfor _, x := range inputs {\n\t\ttasks <- x\n\t}\n\tclose(tasks)\n\n\t// Collect results in order\n\toutput := make([]int, len(inputs))\n\tfor i := 0; i < len(inputs); i++ {\n\t\toutput[i] = <-results\n\t}\n\n\treturn output\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n```",
"passed": 4,
"test_error": ""
}
]