Problem overview
The method addIfMissing receives a shopping list items, an array of strings, and a word item. It adds item at the end only when it isn't in the list yet, and returns the list. When the word is already there, the list comes back as it was. The list may be empty.
With ["milk", "eggs"] and "bread", the answer is ["milk", "eggs", "bread"]. With "milk" instead, nothing is added and the answer is ["milk", "eggs"]. With an empty list and "apples", the answer is ["apples"].
Approach
Ask the array a question, then act on the answer. contains(item) is true when the array holds the word, so !items.contains(item) is true when the word is missing. Only in that case do you call append. Either way, you return the list.
Step by step
- Parameters are constants in Swift, so make a copy that can change:
var items = items. - Write an
ifwhose condition is!items.contains(item). - Inside it, call
items.append(item). This runs only for a new word, so the list never gets the same word twice. - After the
if, returnitems.
The solution in Swift
class Solution {
func addIfMissing(_ items: [String], _ item: String) -> [String] {
var items = items
if !items.contains(item) {
items.append(item)
}
return items
}
}
On the three examples it prints:
["milk", "eggs", "bread"]
["milk", "eggs"]
["apples"]
Language details
- The copy is a real one. Arrays are values in Swift, so appending to the local
itemsdoesn't change the array the caller passed in. - The condition needs no parentheses, and the braces are required even for a single statement.
containscompares whole words and respects letter case, so"Milk"would count as a new word next to"milk". The constraints keep every word lowercase.- On an empty list,
containsis false, so the first word is always added.
Testing
["milk"]with"milk"gives["milk"]: a list of one, word already there.["milk"]with"tea"gives["milk", "tea"]: the new word goes last, not first.["a", "b"]with"b"gives["a", "b"]: the match is the last item, and it still counts.
Running the method twice with the same word on an empty list, addIfMissing(addIfMissing([], "a"), "a"), gives ["a"]. Calling it again with the same word is harmless, which is the point of the check.
Edge cases and common mistakes
- Leaving out the
!. Then the word is appended only when it is already there, which is the opposite of the task. With"bread"nothing would be added. - Appending first, checking later. After the append,
containsis always true, so the check tells you nothing. - Skipping the copy. Calling
appendon the parameter itself is a compile error:cannot use mutating member on immutable value: 'items' is a 'let' constant. - Returning inside the
ifonly. When the word is already there, the method must still return the list.
If you later need this for a big collection where checking has to be fast, a Set answers "is it there?" much faster than an array. The array version is the right fit here because the list must keep its order and stay small.
Cost
contains may look at every item, and append adds to the end. With at most nine words, there is nothing to optimise.
