Start learning free

LESSON 272 OF 677 · PAIRS, SORTING & BUILT-INS

Squares of the Positives in Kotlin: filter, then map

Practice Read the full explanation

You get an ArrayList of integers, nums.

Return a new ArrayList with the square of every number above 0. A number's square is the number times itself.

Keep the squares in the order of their numbers in nums.

Leave out 0 and the negative numbers. If no number is above 0, return an empty ArrayList.

 

Example 1:

Input: nums = [3, -1, 0, 4]
Output: [9, 16]
Explanation: Only 3 and 4 are above 0.
3 times 3 is 9, and 4 times 4 is 16.

Example 2:

Input: nums = [-5, -2, 0]
Output: []
Explanation: No number is above 0.

Example 3:

Input: nums = [1, 2, 3]
Output: [1, 4, 9]
Explanation: Every number is above 0, so every number is squared.

 

Constraints:

  • nums may be empty.
  • Every number is between -1000 and 1000.
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · PAIRS, SORTING & BUILT-INS

    Positive Squares in Kotlin, explained

    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

    1. Build squares in one line: nums.filter { it > 0 }.map { it * it }.
    2. 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 nums to change. Both calls return new lists, and nums stays as given.
    • Trying to add to the result. The result is read-only. If you needed to add to it, call toMutableList() first.
    Keep going with Kotlin500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free