← run

go-01-unique

1.000
3/3 tests· basic
Challenge · difficulty 1/5
# Unique preserving order

Implement **`solution.go`** in `package challenge` exporting:

```go
func Unique(xs []int) []int
```

Return a new slice containing the elements of `xs` with duplicates removed, keeping each value at
the position of its **first occurrence**.

Rules:

- Preserve **first-occurrence order** — do not sort the result.
- Each distinct value appears exactly once.
- Do **not** mutate the input slice.
- For an empty (or nil) input, return an **empty, non-nil** slice (length 0).

Examples:

```go
Unique([]int{3, 1, 3, 2, 1}) // => [3, 1, 2]
Unique([]int{1, 2, 3})       // => [1, 2, 3]
Unique([]int{5, 5, 5})       // => [5]
Unique([]int{})              // => [] (non-nil, length 0)
```
tests/solution_test.go
package challenge

import (
	"reflect"
	"testing"
)

func TestUnique(t *testing.T) {
	cases := []struct {
		name string
		in   []int
		want []int
	}{
		{"removes duplicates preserving order", []int{3, 1, 3, 2, 1}, []int{3, 1, 2}},
		{"already unique unchanged", []int{1, 2, 3}, []int{1, 2, 3}},
		{"all same collapses to one", []int{5, 5, 5}, []int{5}},
		{"empty returns empty", []int{}, []int{}},
		{"single element", []int{42}, []int{42}},
		{"negatives and zero", []int{0, -1, 0, -1, 2}, []int{0, -1, 2}},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := Unique(tc.in)
			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("Unique(%v) = %v, want %v", tc.in, got, tc.want)
			}
		})
	}
}

func TestUniqueReturnsNonNil(t *testing.T) {
	if Unique([]int{}) == nil {
		t.Error("Unique should return a non-nil slice for empty input")
	}
	if Unique(nil) == nil {
		t.Error("Unique should return a non-nil slice for nil input")
	}
}

func TestUniqueDoesNotMutateInput(t *testing.T) {
	in := []int{1, 1, 2}
	_ = Unique(in)
	if !reflect.DeepEqual(in, []int{1, 1, 2}) {
		t.Errorf("input was mutated: got %v", in)
	}
}
Proposed solution
```go
package challenge

func Unique(xs []int) []int {
	seen := make(map[int]struct{}, len(xs))
	result := make([]int, 0, len(xs))
	for _, x := range xs {
		if _, ok := seen[x]; !ok {
			seen[x] = struct{}{}
			result = append(result, x)
		}
	}
	return result
}
```

Test output (stdout)

e":"2026-06-29T19:34:37.569930541+02:00","Action":"output","Package":"challenge","Test":"TestUnique/all_same_collapses_to_one","Output":"--- PASS: TestUnique/all_same_collapses_to_one (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569931731+02:00","Action":"pass","Package":"challenge","Test":"TestUnique/all_same_collapses_to_one","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569932651+02:00","Action":"run","Package":"challenge","Test":"TestUnique/empty_returns_empty"}
{"Time":"2026-06-29T19:34:37.569933541+02:00","Action":"output","Package":"challenge","Test":"TestUnique/empty_returns_empty","Output":"=== RUN   TestUnique/empty_returns_empty\n"}
{"Time":"2026-06-29T19:34:37.569934641+02:00","Action":"output","Package":"challenge","Test":"TestUnique/empty_returns_empty","Output":"--- PASS: TestUnique/empty_returns_empty (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569935621+02:00","Action":"pass","Package":"challenge","Test":"TestUnique/empty_returns_empty","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569939751+02:00","Action":"run","Package":"challenge","Test":"TestUnique/single_element"}
{"Time":"2026-06-29T19:34:37.569940611+02:00","Action":"output","Package":"challenge","Test":"TestUnique/single_element","Output":"=== RUN   TestUnique/single_element\n"}
{"Time":"2026-06-29T19:34:37.569942161+02:00","Action":"output","Package":"challenge","Test":"TestUnique/single_element","Output":"--- PASS: TestUnique/single_element (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569943171+02:00","Action":"pass","Package":"challenge","Test":"TestUnique/single_element","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569944071+02:00","Action":"run","Package":"challenge","Test":"TestUnique/negatives_and_zero"}
{"Time":"2026-06-29T19:34:37.569944891+02:00","Action":"output","Package":"challenge","Test":"TestUnique/negatives_and_zero","Output":"=== RUN   TestUnique/negatives_and_zero\n"}
{"Time":"2026-06-29T19:34:37.569945951+02:00","Action":"output","Package":"challenge","Test":"TestUnique/negatives_and_zero","Output":"--- PASS: TestUnique/negatives_and_zero (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569947101+02:00","Action":"pass","Package":"challenge","Test":"TestUnique/negatives_and_zero","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569948151+02:00","Action":"output","Package":"challenge","Test":"TestUnique","Output":"--- PASS: TestUnique (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569949191+02:00","Action":"pass","Package":"challenge","Test":"TestUnique","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569951341+02:00","Action":"run","Package":"challenge","Test":"TestUniqueReturnsNonNil"}
{"Time":"2026-06-29T19:34:37.569952271+02:00","Action":"output","Package":"challenge","Test":"TestUniqueReturnsNonNil","Output":"=== RUN   TestUniqueReturnsNonNil\n"}
{"Time":"2026-06-29T19:34:37.569953501+02:00","Action":"output","Package":"challenge","Test":"TestUniqueReturnsNonNil","Output":"--- PASS: TestUniqueReturnsNonNil (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569955331+02:00","Action":"pass","Package":"challenge","Test":"TestUniqueReturnsNonNil","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569956221+02:00","Action":"run","Package":"challenge","Test":"TestUniqueDoesNotMutateInput"}
{"Time":"2026-06-29T19:34:37.569957101+02:00","Action":"output","Package":"challenge","Test":"TestUniqueDoesNotMutateInput","Output":"=== RUN   TestUniqueDoesNotMutateInput\n"}
{"Time":"2026-06-29T19:34:37.569958541+02:00","Action":"output","Package":"challenge","Test":"TestUniqueDoesNotMutateInput","Output":"--- PASS: TestUniqueDoesNotMutateInput (0.00s)\n"}
{"Time":"2026-06-29T19:34:37.569959771+02:00","Action":"pass","Package":"challenge","Test":"TestUniqueDoesNotMutateInput","Elapsed":0}
{"Time":"2026-06-29T19:34:37.569960921+02:00","Action":"output","Package":"challenge","Output":"PASS\n"}
{"Time":"2026-06-29T19:34:37.570134788+02:00","Action":"output","Package":"challenge","Output":"ok  \tchallenge\t0.001s\n"}
{"Time":"2026-06-29T19:34:37.570147048+02:00","Action":"pass","Package":"challenge","Elapsed":0.001}