Problem overview
positiveSquares receives a list of integers nums and returns a new list with the square of every number above 0, in the original order. Zero and negative numbers are left out. If nothing is above 0, or the list is empty, the result is an empty list.
For [3, -1, 0, 4] the result is [9, 16]. For [-5, -2, 0] it is []. For [1, 2, 3] it is [1, 4, 9]. Numbers are between -1000 and 1000, so a square is at most 1,000,000 and fits in an Int easily.
Approach
Two jobs: pick some numbers, and change them. filter picks, map changes, and chaining them runs the picking first. In each lambda, it is the current number.
The order of the two calls matters. Filter first, on the original numbers, with it > 0. Then map each survivor to it * it. If you square first, every negative number becomes positive: -3 turns into 9, passes the test, and ends up in the result although it shouldn't.
Step by step
- Build
squaresin one line:nums.filter { it > 0 }.map { it * it }. - Return
squares.
No special case is needed for the empty result: filtering can produce an empty list, and mapping an empty list gives an empty list.
What each call produces
You can split the chain to see the two stages. For [3, -1, 0, 4]:
val kept = nums.filter { it > 0 }
println(kept)
println(kept.map { it * it })
[3, 4]
[9, 16]
The filter removes -1 and 0, and the map squares what is left. The chain in the solution is these two lines joined, with no named middle list.
The solution in Kotlin
class Solution {
fun positiveSquares(nums: List<Int>): List<Int> {
val squares = nums.filter { it > 0 }.map { it * it }
return squares
}
}
On the examples it returns:
[9, 16]
[]
[1, 4, 9]
An empty input also gives [].
The same with a loop
A loop with an if does the same job, and shows what the chain hides:
val squares = mutableListOf<Int>()
for (n in nums) {
if (n > 0) {
squares.add(n * n)
}
}
For [3, -1, 0, 4] it also gives [9, 16]. The if is the filter and the add is the map. The chain makes two passes over the list, the loop one, but both grow in step with the list length, and the chain reads closer to the statement. The largest possible square, 1000 * 1000, is 1000000.
Why the range of the numbers matters
An Int is 32 bits, so it holds values up to about 2.1 billion before it wraps around. Numbers up to 1000 square to at most 1,000,000, well inside that, so no overflow can happen here. With numbers around 50,000 the square would pass 2.1 billion and the result would be wrong without any error.
Edge cases and common mistakes
- Squaring first. With
nums.map { it * it }.filter { it > 0 }the first example gives[9, 1, 16]: -1 was squared into 1 before the filter ran. Zero is dropped either way, but the negatives are not. - Using
>= 0. That keeps 0, and the result would contain a 0 square. The statement says to leave out 0. - Expecting
numsto change. Both calls return new lists, andnumsstays as given. - Trying to add to the result. The result is read-only. If you needed to add to it, call
toMutableList()first.
