Problem overview
Shout gets a List<string> named words, every word in lowercase letters a to z. It returns a new List<string> where each word is in capital letters with ! added at the end, in the same order. ["hey", "you"] becomes ["HEY!", "YOU!"], and ["no", "no", "no"] becomes ["NO!", "NO!", "NO!"]: repeated words are shouted each time.
Approach: one rule for every item
This is a transform: the output has exactly one item for each input item, and each output item depends only on its own input. LINQ's Select is made for that. It takes a lambda that turns one item into a new one, and it applies the lambda to every item in order.
Here the lambda is word => word.ToUpper() + "!". ToUpper() returns a capitalised copy of the string, because C# strings are immutable and never change in place. The + "!" then adds the exclamation mark to that copy.
Select returns a sequence, not a List. Ending the chain with ToList() builds the List<string> that the method must return.
Step by step
- Build
shouted, aList<string>, withwords.Select(...)and the lambda above, then.ToList(). - Return
shouted. The input listwordswas not changed.
The solution in C#
class Solution {
public List<string> Shout(List<string> words) {
List<string> shouted = words.Select(word => word.ToUpper() + "!").ToList();
return shouted;
}
}
In a file of your own, Select and ToList need using System.Linq;.
Details that matter
Selectis lazy. WithoutToList()nothing is computed until something reads the sequence, and it reads the list as it is then. If you writevar sh = sw.Select(q => q.ToUpper());forsw = ["hey"], then add"you"tosw, printingshgives[HEY, YOU].ToList()takes the snapshot at the moment you call it.- No item is skipped.
Selectalways produces as many items as it gets. To drop items, useWhere. - The precedence of
+.word.ToUpper() + "!"reads as "the shouted word, then the mark". There is no need for parentheses.
The same thing with a loop
Before LINQ, you would write this transform by hand: make an empty list, visit each word, and add the shouted version.
List<string> shouted = new List<string>();
foreach (string word in words) {
shouted.Add(word.ToUpper() + "!");
}
return shouted;
It returns [HEY!, YOU!] for ["hey", "you"], the same as the Select version. The loop version is longer, but it is exactly what Select does for you. The pattern is always the same: an empty result, one pass, one Add per item. When the rule for each item is a single expression, Select says the same thing in one line, with no list to declare and no Add to forget.
The result is a separate list from the input. Changing one later, with Add or Remove, never affects the other. That is usually what a caller expects from a method that "returns a new list".
Testing
Outputs from running the solution:
| words | Result |
|---|---|
["hey", "you"] | [HEY!, YOU!] |
["go"] | [GO!] |
["no", "no", "no"] | [NO!, NO!, NO!] |
Common mistakes
- Calling
word.ToUpper()on its own line. The call returns the new string. If you ignore it, the original word is still lowercase. - Forgetting
ToList(). The method's return type isList<string>, and aSelectresult alone is not one. - Adding the exclamation mark to the list. The
!belongs inside the lambda, once per word.
