1
\$\begingroup\$

I have the following code which collects gaps in a number sequence into a separate vec.

There will never be a case, iterating over a Vec<i32> where .windows(2) will yield a window with None values. So I want to simplify things by transforming each window into a Gap with first and last i32 values wherever the gap between 2 numbers is > 1. So a transform -> filter -> result kind of pipeline.

My version works, but seems imperative and long-winded. I keep thinking I should be able to use .from_fn(), .filter() and .collect() to generate the gap vector without a for loop. How would I go about this?

#[derive(Debug)]
struct Gap {
    first: i32,
    last: i32,
}

fn main() {
    let original_numbers = vec![1, 3, 4, 7, 8, 10, 11, 13, 6, 19, 20, 21];
    let mut seq = original_numbers.clone();
    seq.sort();
    let windows = seq.windows(2);
    let mut gaps: Vec<Gap> = Vec::new();
    for gap_window in windows {
        let g = Gap {
            first: if let Some(num) = gap_window.first() { *num + 1 } else { 0 },
            last: if let Some(num) = gap_window.last() { *num - 1 } else { 0 },
        };
        if g.last - g.first >= 0 {
            gaps.push(g)
        }
    }
    for gap in gaps {
        println!("{:?}", gap)
    };

}

Output:

Gap { first: 2, last: 2 }
Gap { first: 5, last: 5 }
Gap { first: 9, last: 9 }
Gap { first: 12, last: 12 }
Gap { first: 14, last: 18 }
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

My version works, but seems imperative and long-winded. I keep thinking I should be able to use .from_fn(), .filter() and .collect() to generate the gap vector without a for loop. How would I go about this?

Use filter_map. It combines filter and map, and what you are doing is essentially mapping windows into gaps and filtering out invalid gaps.

first: if let Some(num) = gap_window.first() { *num + 1 } else { 0 },

Rust has nice functions for dealing with Option. In this case you can do:

first: gap_window.first().map(|num| *num  + 1).unwrap_or(0)

But why are defaulting to zero here? Silently defaulting to garbage values when an unexpected outcome occours is the worst possible strategy. It is way way better to panic:

first: *gap_window.first().unwrap() + 1

Or even better:

first: gap_window[0] + 1
\$\endgroup\$
3
  • \$\begingroup\$ good pointers. filter_map() not appropriate because the Gap structs are needed later on, but good to know about filter_map() for the same project. How does gap_window[0] + 1 unwrap the Option? \$\endgroup\$
    – Kim
    Commented Oct 18, 2022 at 18:37
  • \$\begingroup\$ The fact that Gap structs are needed later doesn't prevent filter_map from working. See play.rust-lang.org/…. But in this case filter and then map is actually better. \$\endgroup\$ Commented Oct 18, 2022 at 21:25
  • \$\begingroup\$ @Nick, gap_window[0] effectively means gap_window.get(0).unwrap() The language designers decided that when indexing into slices it would panic on out of bounds rather than returning an option. \$\endgroup\$ Commented Oct 18, 2022 at 21:28
1
\$\begingroup\$

Thanks to Winston Ewert's answer. My final version below. Note that filter_map() was not appropriate because the output and the filter expression are different.

#[derive(Debug)]
struct Gap {
    first: i32,
    last: i32,
}

fn main() {
    let original_numbers = vec![1, 3, 4, 7, 8, 10, 11, 13, 6, 19, 20, 21, 22];
    let mut seq = original_numbers.clone();
    seq.sort();
    let windows = seq.windows(2);
    let mut gaps = windows.into_iter().map(|win| Gap {
            first: win.first().unwrap() + 1,
            last: win.last().unwrap() - 1
        }).filter(|gap| gap.last - gap.first >= 0);
    for gap in gaps {
        println!("{:?}", gap)
    };
}

Output:

Gap { first: 2, last: 2 }
Gap { first: 5, last: 5 }
Gap { first: 9, last: 9 }
Gap { first: 12, last: 12 }
Gap { first: 14, last: 18 }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.