19

I want to split a Vec into some parts of equal length, and then map over them. I have an iterator resulting from a call to Vec's chunks() method. This may leave me with a part that will be smaller than other parts, which will be the last element generated by it.

To be sure that all parts have equal length, I just want to drop that last element and then call map() on what's left.

hoijui
  • 3,615
  • 2
  • 33
  • 41
Innot Kauker
  • 1,333
  • 1
  • 18
  • 30
  • 2
    although there's a better solution for your case, note the existence of [std::iter::take](https://doc.rust-lang.org/std/iter/struct.Take.html) – JHBonarius Oct 04 '18 at 19:24

5 Answers5

15

As Sebastian Redl points out, checking the length of each chunk is the better solution for your specific case.

To answer the question you asked ("Use all but the last element from an iterator"), you can use Iterator::peekable to look ahead one. That will tell you if you are on the last item or not and you can decide to skip processing it if so.

let things = [0, 1, 2, 3, 4];

let mut chunks = things.chunks(2).peekable();
while let Some(chunk) = chunks.next() {
    if chunks.peek().is_some() {
        print!("Not the last: ");
    } else {
        print!("This is the last: ")
    }

    println!("{:?}", chunk);
}

To be sure that all parts have equal length, I just want to drop that last element

Always dropping the last element won't do this. For example, if you evenly chunk up your input, then always dropping the last element would lose a full chunk. You'd have to do some pre-calculation to decide if you need to drop it or not.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
11

You can filter() the chunks iterator on the slice's len() being the amount you passed to chunks():

let things = [0, 1, 2, 3, 4];

for chunk in things.chunks(2).filter(|c| c.len() == 2) {
    println!("{:?}", chunk);
}

This prints

[0, 1]
[2, 3]

As of Rust 1.31, you can use the chunks_exact method as well:

let things = [0, 1, 2, 3, 4];

for chunk in things.chunks_exact(2) {
    println!("{:?}", chunk);
}

This prints

[0, 1]
[2, 3]

Note that the returned iterator also has the method remainder if you need to get the uneven amount of items at the very end.

JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
3

If the collection being iterated over implements std::iter::DoubleEndedIterator, you can use the next_back() method for a solution that is (probably) marginally more performant than Shepmaster's solution and a little cleaner:

let things = [0, 1, 2, 3, 4];

let mut chunks = things.chunks(2);
let last = chunks.next_back().unwrap();
    
println!("Last: {:?}", last);
    
for chunk in chunks {
    println!("Not last: {:?}", chunk);
}

next_back() eats the last element of the iterator, so after calling next_back() the iterator can be used to iterate over everything else. The output of the segment above:

Last: [4]
Not last: [0, 1]
Not last: [2, 3]
tedtanner
  • 577
  • 6
  • 19
  • 1
    Seems like a reasonable solution when your iterator implements `DoubleEndedIterator` (but not all iterators do!). – Shepmaster Dec 21 '22 at 20:43
  • @Shepmaster Good point. The OP was using a `Vec` so no problems in his/her scenario, but the question title was for iterators generally so the answer should be general. I have updated my answer to mention the requirement. – tedtanner Dec 22 '22 at 20:08
  • The last part may only be dropped, if it is smaller than the choosen chunk `size` 2 and `things` is not empty: `if (things.len() % size) != 0 { chunks.next_back(); }` – Kaplan Jan 14 '23 at 15:54
1

Since Rust 1.31.0, you can use chunks_exact() to leave out the last chunk if it is not full.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
1

For this particular situation, chunks_exact is what you want to use right now.

For the more general case of "taking all but the last n items of an iterator", Itertools has a method just for this: dropping_back.

Note that unlike methods of std::iter::Iterator, dropping_back is evaluated eagerly. Take care if your iterator chain is computation-heavy.

cyqsimon
  • 2,752
  • 2
  • 17
  • 38