Problem overview
positiveSquares gets an ArrayBuffer[Int] called nums, which may be empty, with numbers from -1000 to 1000. It returns a new buffer with the square of every number above 0, in the order of the numbers. Zero and negative numbers are left out, and if nothing is above 0, the result is an empty buffer.
[3, -1, 0, 4]gives[9, 16].[-5, -2, 0]gives[].[1, 2, 3]gives[1, 4, 9].
Approach
Two things happen: some numbers are dropped, and the rest are changed. Dropping is filter, and changing is map. Chain them, with filter first: keep the numbers above 0, then square what is left.
The order matters. Squaring first turns -3 into 9, and 9 is above 0, so a filter after the squaring cannot tell that the number was negative. For [3, -1, 0, 4], squaring first and filtering second gives ArrayBuffer(9, 1, 16), with the 1 that came from -1. Filtering first gives the right ArrayBuffer(9, 16).
Step by step
- Call
filteronnumswithnum => num > 0. Zero is not above 0, so it is left out too. - Call
mapon that result withnum => num * num. - Keep the result in a
valnamedsquaresand return it.
The solution in Scala
class Solution {
def positiveSquares(nums: ArrayBuffer[Int]): ArrayBuffer[Int] = {
val squares = nums.filter(num => num > 0).map(num => num * num)
return squares
}
}
A few details:
- The chain reads left to right: filter, then map. Each call returns a new
ArrayBuffer, andnumsis unchanged. - When no number passes,
filterreturns an empty buffer, andmapon an empty buffer returns an empty buffer. The empty input works the same way, so no special case is needed. - The largest square is 1000 times 1000, which is 1,000,000. It fits easily in an
Int, whose limit is a little over two billion.
The same thing with a loop
The loop version shows what the chain does in one pass:
val squares = ArrayBuffer[Int]()
for (num <- nums) {
if (num > 0) {
squares += num * num
}
}
For [3, -1, 0, 4] it gives ArrayBuffer(9, 16), the same as the chain. The if is the filter and the num * num is the map. The chain builds an in-between buffer after filter, which is fine at this size and keeps each idea on its own call.
Scala also has collect, which can do both in one call: nums.collect { case n if n > 0 => n * n } returns ArrayBuffer(9, 16) as well. The filter and map pair is the form this lesson teaches.
Testing
[3, -1, 0, 4] ArrayBuffer(9, 16)
[-5, -2, 0] ArrayBuffer()
[1, 2, 3] ArrayBuffer(1, 4, 9)
[] ArrayBuffer()
Edge cases
- Empty input. Both calls return an empty buffer, so the function returns
ArrayBuffer()without any special check. - Repeated numbers. Nothing removes duplicates. A buffer holding 2 twice gives 4 twice.
- Only zeros and negatives. The filter removes everything, and the answer is the empty buffer the lesson asks for.
- All positive. The filter keeps everything, and the result is just the square of each number in the original order.
Common mistakes
- Mapping before filtering. The squares are never negative, so the filter drops nothing useful, as shown above.
- Using
>= 0. That keeps 0 and returns an extra 0 in the result. - Squaring with
^. In Scala^is a bitwise operator, not a power. Usenum * num.
