Problem overview
The function positiveSquares receives an array of integers, nums, which may be empty. It returns a new array with the square of every number above 0, in the same order as the numbers in nums. Zero and negative numbers are left out. If nothing is above 0, the result is an empty array.
For [3, -1, 0, 4] the result is [9, 16]: only 3 and 4 are above 0. For [-5, -2, 0] it is [].
Approach
Two things happen to the numbers: some are dropped, and the rest are changed. Swift has a method for each. filter keeps the items for which a closure returns true. map replaces every item with the closure's result. You can chain them, so the output of filter goes straight into map.
The order matters. Filter first, then square. If you square first, -3 becomes 9, which passes a test for "above 0" and sneaks into the result. For [3, -1, 0, 4], squaring first and then keeping the numbers above 0 gives [9, 1, 16], which is wrong. Zero is not above 0, so the test is > 0, not >= 0.
You could also write this with a loop: start with an empty array, go through nums, and append num * num when num > 0. The chain says the same thing in one line, and it reads as the two ideas in the statement: keep some numbers, then change them.
Step by step
- Keep the numbers above 0 with
nums.filter { $0 > 0 }. For the first example this leaves[3, 4]. - Square each one with
.map { $0 * $0 }on the filtered array. The result is[9, 16]. Both steps go in one line, in a constant namedsquares. - Return
squares. When no number is above 0,filtergives an empty array,mapof an empty array is empty, and that is the right answer.
The solution in Swift
class Solution {
func positiveSquares(_ nums: [Int]) -> [Int] {
let squares = nums.filter { $0 > 0 }.map { $0 * $0 }
return squares
}
}
Swift details:
- Each closure has its own
$0. In thefilterclosure,$0is a number fromnums. In themapclosure,$0is a number that passed the filter. - The
filterclosure must return aBool.{ $0 > 0 }does. A closure such as{ $0 * 2 }doesn't compile there: the compiler reports that it cannot convertIntto the closure result typeBool. - Nothing is modified.
filterandmapeach return a new array, andnumsstays as it was. - Squares fit in
Int. The numbers are between -1000 and 1000, so a square is at most 1,000,000.
Each step produces a whole array before the next one starts. For [3, -1, 0, 4], the filter gives [3, 4], and the map turns that into [9, 16]. Seeing the middle array helps when a result is wrong: if the filter's output already contains the wrong numbers, the problem is in the first closure, not the second.
Edge cases and common mistakes
- An empty array.
[]gives[]. - Only zero and negatives.
[-5, -2, 0]gives[]. - All positive.
[1, 2, 3]gives[1, 4, 9]. - Using
>= 0. That would keep 0 and add a0to the result. - Mapping before filtering. As shown above,
nums.map { $0 * $0 }.filter { $0 > 0 }returns[9, 1, 16]for the first example, because the negative number was squared into a positive one.
Testing
[3, -1, 0, 4]gives[9, 16].[-5, -2, 0]gives[].[1, 2, 3]gives[1, 4, 9].[]gives[].
