Calamine crate (Rust) only reads first row of xlsx file, how to fix?

Viewed 414

I have taken an interest in Rust as a hobby (I'm a hobbyist programmer, I have used python and C++ to automate things at work), and I'm currently trying to practice by porting some of my python code for data analysis to rust, however it has proven to be quite difficult to me as I'm a complete noob at this and most resources and documentation tend more towards intermediate and advanced rust users (at least for data science).

I'm trying to use the calamine crate to load a xlsx file into a Vec, however only the first row is parsed when using .next() method of the iterator in a for loop, so I'm clearly doing something wrong. As extra information which may be of help, the xlsx file consists of 23851 rows and 28 columns, and the actual data (not considering headers, which are not the be read) begins at position A3.

    let range = excel
        .worksheet_range("Sheet0")
        .ok_or(calamine::Error::Msg("Cannot find Sheet0"))??;
    
    let start = range.start().unwrap();
    let end = range.end().unwrap();
    
    // First cell to be read starts at 'A3', theres probably a better way to do this
    let range = range.range((start.0 + 2, start.1), end);

    println!("Start: {:?} - End: {:?}", range.start(), range.end());

    let mut iter = RangeDeserializerBuilder::new().has_headers(false).from_range::<_, mr::RawMR>(&range)?;

    let mut df: Vec<mr::RawMR> = Vec::new();

    for result in iter.next() {
        match result {
            Ok(row) => { df.push(row); }
            Err(e) => println!("Error: {}", e), };
    }
    
    // This prints 'Size: 1', I was expecting the total number of rows
    println!("Size: {:?}", df.len());

This code only manages to read the first line of the excel file, and thus the vec contains only the first row.

Any help is greatly appreciated.

Thanks in advance

1 Answers

The mistake I see in your code is how you treat the iterator. next() will return one element every time it is called. To get the next element it must be called one more time.

The loop

for result in iter.next() {
    match result {
        Ok(row) => { df.push(row); }
        Err(e) => println!("Error: {}", e), };
}

will only yield one result as the list received by for in is the return value from iter.next()

If you replace it with either only the iterator or a while let Some()=iter.next().

for result in iter {

  ...
}

for ... in will run over the iterator until next returns None, but you should not call it.

In the alternative while let the assignment is executed every time the loop restarts. Hence next() is needed

while let Some(result) = iter.next() {
  ...
}

You should be able to iterate over the entire space.

Related