Start learning free

LESSON 274 OF 679 · TUPLES, SORTING & BUILT-INS

Positive Squares in Rust: filter, map and collect

Practice Read the full explanation

You get a Vec of integers, nums.

Return a new Vec 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 Vec.

 

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

    RUST · TUPLES, SORTING & BUILT-INS

    Positive Squares in Rust, explained

    Problem overview

    The function positive_squares receives a vector of integers, nums, which may be empty, with every number between -1000 and 1000. It returns a new vector with the square of every number above 0, in the order of the original numbers. Zero and the negative numbers are left out, and if nothing is above 0 the result is an empty vector.

    For [3, -1, 0, 4] the answer is [9, 16]. For [-5, -2, 0] it is [], and for [1, 2, 3] it is [1, 4, 9].

    Approach

    There are two jobs: keep some numbers, and transform the ones that stay. An iterator chain does both. filter keeps the items for which its closure returns true, and map turns each remaining item into a new value. The order matters: filter first, then square. Squaring first would turn -3 into 9, which then passes the "above 0" test and ends up in the answer.

    That wrong order really does give a wrong result. On [3, -1, 0, 4], squaring first and then keeping values above 0 produces [9, 1, 16]: the 1 comes from -1.

    Step by step

    1. Start from nums.into_iter(). This hands over the numbers themselves and uses up the vector, which is fine because the function returns a new one.
    2. Filter with |num| *num > 0. The closure receives a reference to each item, so the test reads *num. Without the star the compiler reports mismatched types, because a reference is being compared with a number.
    3. Map with |num| num * num. After filter, the items passing through are plain numbers again, so no star is needed here.
    4. Collect into a Vec<i32> named squares. The type after the name tells collect() what to build.
    5. Return squares. When no number is above 0, the vector is empty, and that is the right answer.

    The solution in Rust

    impl Solution {
        fn positive_squares(nums: Vec<i32>) -> Vec<i32> {
            let squares: Vec<i32> = nums.into_iter().filter(|num| *num > 0).map(|num| num * num).collect();
            return squares;
        }
    }

    The numbers are at most 1000 in size, so a square is at most 1,000,000 and fits easily in an i32. An empty input vector just produces an empty output: the chain has nothing to process.

    Why the extra star

    The closures in a chain receive different things. map gets each item by value, so with into_iter() it gets a plain i32. filter only inspects an item and must hand it on, so it passes the closure a reference to the item. That is why one closure uses *num and the other does not, even though both are named num.

    Another way

    The same result with a loop and an if, which is the form to reach for when the logic grows beyond two small steps:

    let mut out = Vec::new();
    for num in nums {
        if num > 0 {
            out.push(num * num);
        }
    }

    On [3, -1, 0, 4] it also gives [9, 16]. The iterator chain says the same thing in one line: keep, then transform, then gather.

    Testing

    The three examples and an empty vector give:

    [9, 16]
    []
    [1, 4, 9]
    []

    Edge cases and common mistakes

    • Zero. The test is strictly above 0, so 0 is dropped. Writing >= 0 would add a 0 to the answer.
    • Squaring before filtering, as shown above.
    • Forgetting collect(), which leaves an iterator instead of a vector.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free