Problem overview
The function addIfMissing receives a shopping list items, a slice of strings, and a word item. It adds item at the end only when the list doesn't hold it yet, and returns the list. When the word is already there, the list comes back as it was. The list may be empty.
For ["milk", "eggs"] and "bread" the result is [milk eggs bread]. For "milk" it stays [milk eggs]. For an empty list and "apples" it becomes [apples].
Approach
The task is a check followed by an action. The check is a membership test, which slices.Contains answers with a bool. The action is append. Put ! in front of the test, so the action runs only when the word is missing, and the list never gets the same word twice.
The membership test reads the whole list, from the first word to the last, and stops at the first match. For a shopping list of a few words that is instant. Doing the test before the append is what keeps the list free of duplicates, and the order of the words is untouched: new words always go at the end.
Step by step
- Write an
ifwhose condition is!slices.Contains(items, item), true when the word is missing. - Inside it, append
itemtoitemsand assign the result back, sinceappendreturns the new slice. - After the
if, returnitems.
The solution in Go
func addIfMissing(items []string, item string) []string {
if !slices.Contains(items, item) {
items = append(items, item)
}
return items
}
There is no else: when the word is present, nothing needs to happen, and the function simply returns the slice it received.
The assignment items = append(items, item) changes only the function's own variable. That is why the new slice is returned and the caller uses the return value. A helper that appended to its parameter and returned nothing would leave the caller's slice unchanged.
An empty list is not special. slices.Contains on an empty slice is false, so the word is appended. A slice that is nil, with no memory behind it yet, behaves the same way: addIfMissing(nil, "tea") returns [tea].
Testing
| items | item | result |
|---|---|---|
[milk eggs] | bread | [milk eggs bread] |
[milk eggs] | milk | [milk eggs] |
[] | apples | [apples] |
All three rows come from running the function.
Edge cases and common mistakes
Three situations are worth checking by hand: a new word, a word already present, and an empty list. The table above covers all three. A word that appears in the middle of the list counts as present just like one at the start or end, because slices.Contains looks at every position.
- Dropping the
!. The word would be added only when it is already there, so new words never get in. - Appending without the check.
["milk"]becomes[milk milk]. - Comparing with the wrong case. The words are all lowercase letters, so
"Milk"would count as a different word. The constraints keep this from happening here. - Leaving out the assignment from
append. Writing justappend(items, item)is a compile error:append(items, item) (value of type []string) is not used.
