Problem overview
The function shout receives an array of lowercase words and returns a new array in which every word is in capital letters with ! added at the end. The order stays the same, and a repeated word is shouted each time. So ["hey", "you"] becomes ["HEY!", "YOU!"], and ["no", "no", "no"] becomes ["NO!", "NO!", "NO!"].
Approach
This is a "change every item" job, and that is what map does. It runs a closure on each item of an array and collects the results in a new array of the same length and order. The closure here receives one word, written $0, and returns the shouted version.
To shout a word, call uppercased() on it, which returns a new string in capital letters, and add "!" with +. In Swift, strings are values, so the original word and the original array are not changed.
With ["hey", "you"], the closure runs twice. The first call receives "hey" and returns "HEY!". The second receives "you" and returns "YOU!". map puts the two results in a new array, in the same order as the words that produced them. Because every word produces exactly one result, the new array always has as many items as words.
Step by step
- Build
shoutedwithwords.map. The closure{ $0.uppercased() + "!" }turns each word into its shouted form. One word goes in and one comes out, in the same place. - Return
shouted. The new array is the answer, andwordsitself didn't change.
The solution in Swift
class Solution {
func shout(_ words: [String]) -> [String] {
let shouted = words.map { $0.uppercased() + "!" }
return shouted
}
}
Swift details:
$0is the current item. It is a shorthand for a closure's first parameter. The same closure can be written with a name:words.map { word in word.uppercased() + "!" }.- The result type is inferred. The closure returns a
String, soshoutedis a[String], which matches the function's return type. uppercased()is a method call. The parentheses are required. Without them, the compiler reports "function produces expected type 'String'; did you mean to call it with '()'?".- Plain
+joins strings."HEY" + "!"is"HEY!".
A loop does the same job with more lines: create an empty [String], append word.uppercased() + "!" for each word, and return it. map says the same thing in one line, and it is the usual choice when each item becomes exactly one new item.
When you do want a loop, the result array has to be created first with its type, as var out = [String](). With map there is nothing to declare or append to, which removes a common source of mistakes such as forgetting to return the array.
Edge cases and common mistakes
- One word.
["go"]gives["GO!"]. - Repeated words.
["no", "no", "no"]gives three"NO!".mapnever merges or drops items. - Putting the
!before the conversion.($0 + "!").uppercased()gives the same result here, because!has no capital form. The order matters for other characters, but not in this lesson. - Expecting
wordsto change.mapreturns a new array. After the call,wordsstill holds the lowercase words, so returnshouted, notwords.
Testing
["hey", "you"]gives["HEY!", "YOU!"].["go"]gives["GO!"].["no", "no", "no"]gives["NO!", "NO!", "NO!"].
