Problem overview
positiveSquares(nums) receives an array of integers, possibly empty, and returns a new array with the square of every number above 0. A square is the number times itself. The squares keep the order of their numbers, and 0 and the negative numbers are left out. If no number is above 0, the answer is an empty array.
For [3, -1, 0, 4] the answer is [9, 16]: only 3 and 4 are above 0. For [-5, -2, 0] it is [], and for [1, 2, 3] it is [1, 4, 9].
Approach
There are two actions: choose some of the numbers, and change each chosen number. filter chooses and map changes, so the solution chains them. The order matters. If you square first, every result is already non-negative and no longer tells you whether the original number was positive: -3 would become 9 and pass the filter. So choose first, then square.
Step by step
- Call
nums.filterwith an arrow function that is true whennum > 0. This drops 0 and the negative numbers. - Call
.mapon that result with an arrow function that turns eachnumintonum * num. - Store the result in
squaresand return it.
The solution in JavaScript
class Solution {
positiveSquares(nums) {
let squares = nums.filter(num => num > 0).map(num => num * num);
return squares;
}
}
Both methods return new arrays, so nums is not changed, and the order of the kept numbers carries through to the squares. The three examples, plus the empty array, return:
[9,16]
[]
[1,4,9]
[]
The last line is for []: filtering an empty array gives an empty array, and mapping it gives an empty array again. No special case is needed.
Complexity
The filter reads every number once, and the map reads each kept number once, so the time is O(n) for n numbers. Numbers between -1000 and 1000 square to at most 1,000,000, which a JavaScript number holds exactly. The result and the array between the two calls take O(n) extra space.
Why not a loop
A loop with an if and a push works too, and gives the same answers. The chained version states the two actions, choose and change, directly in one line, which is why the lesson uses it. Choose whichever you can read more easily, as long as the order of the actions is right.
Edge cases and common mistakes
- Squaring before filtering. On
[-3, 0, 3, 4],nums.map(n => n * n).filter(n => n > 0)gives[9, 9, 16], one value too many. Filtering first gives[9, 16]. - Keeping 0. A test of
num >= 0keeps 0, and its square 0 would appear in the answer. The statement says above 0. - Using the square as the test.
filter(n => n * n)keeps every number except 0 (any non-zero number gives a value that counts as true), so the negatives stay.[3, -1, 0, 4]would give[3, -1, 4]. - Trying to put the test inside
map.mapalways returns one value per item, so it can't skip a number. Withn => n > 0 && n * nyou get[9, false, false, 16].
