Problem overview
removeRepeats receives a slice of integers nums and returns a new slice with each value once. The first copy of each value is kept, later copies are dropped, and the values stay in the order they first appear.
For [3, 1, 3, 2, 1] the answer is [3 1 2]: the second 3 and the second 1 are repeats. For [6, 6, 6, 6] it is [6], and for [4, 9, 2], with no repeats, nothing changes.
Approach
Read the numbers from left to right and remember which ones have appeared. A number that isn't remembered yet is a first copy, so it goes into the result. One that is remembered is a repeat and is skipped. Reading in order is what keeps the first copies and preserves their order.
Go has no set type, so a map[int]bool plays that role. A key that isn't in the map reads as false, which makes !seen[num] true exactly for a new value.
Step by step
- Create the map
seen := map[int]bool{}. - Create
result := []int{}. It starts empty andappendgrows it. - Loop over
numswithfor _, num := range nums. - If
!seen[num], appendnumtoresult(assigning the longer slice back) and setseen[num] = true. - Return
resultafter the loop.
The solution in Go
func removeRepeats(nums []int) []int {
seen := map[int]bool{}
result := []int{}
for _, num := range nums {
if !seen[num] {
result = append(result, num)
seen[num] = true
}
}
return result
}
append may allocate a bigger backing array and returns the slice to use, so its result must be assigned back to result. Calling append(result, num) on its own line would discard the new element.
Here is what the loop does with [3, 1, 3, 2, 1], showing each number and the result after it:
3 new -> [3]
1 new -> [3 1]
3 repeat -> [3 1]
2 new -> [3 1 2]
1 repeat -> [3 1 2]
Each number is checked against seen before it is recorded, so the first 3 passes and the second 3 does not. Since the slice is built in reading order, the surviving numbers keep their relative order.
The set makes each check cost about one step, so the whole function reads the input once. Without a set you would search the result slice for every number, which gets slower as the slice grows.
Testing
[3 1 3 2 1] -> [3 1 2]
[6 6 6 6] -> [6]
[4 9 2] -> [4 9 2]
Another way to remove repeats is to scan the result slice before each append. That also gives the right answer, but each scan grows with the result, so large inputs get slow. The set keeps the check at one lookup, and the memory cost is one map entry per different value. The result slice is also never nil here: it starts as []int{}, so the function returns an empty but valid slice when given one.
Common mistakes
- Looping over the map to build the result. Map order is random in Go, so the output order would not match the order in
nums. Build the result from the slice and use the map only for lookups. - Returning inside the loop. That returns after the first number. The
returngoes after the loop. - Forgetting to mark the number as seen. Without
seen[num] = trueevery number looks new and nothing is removed. - Not assigning the result of
append. The slice stays empty.
