6
votes

I have an iterator iter; is it possible to convert it into an iterator that iterates over each Nth element? Something like iter.skip_each(n - 1)?

2
There's a step iterator in itertools: bluss.github.io/rust-itertools/doc/itertools/…Dogbert
Nice, though I hoped for something from standard library. Setting in which I use Rust currently makes it... inconvenient to use external crates.anula
You'll have a really bad time then. Much work has been invested to make using third party crates simple, and so everyone works under the assumption that adding a dependency is a no brainer. This is not just a cultural thing, it's actively used to justify omitting things from the standard library. I have no idea what your circumstances are but you should try hard to fix that.user395760
There's a pile of hard-to-fix reasons that one can't easily use external crates, both technical and otherwise. For instance, one might be on a locked-down corporate network (or an otherwise unreliable/slow connection), or have code review requirements for external code (e.g. for licensing). There is ongoing work in cargo itself to give people more tools for the first case.huon
Hm, could you expand? In my experience cargo is one of the nicest cross-compilation experiences I've ever had, as it's just cargo build --target ..., and (if you're using std/core) doubly so with the new rustup that makes installing the standard library for new targets quite easy. It definitely gets much more annoying with crates with C dependencies, but pure Rust things like itertools should slot in seemlessly.huon

2 Answers

8
votes

As of Rust 1.26, there is the Iterator::step_by method in the standard library:

Basic usage:

let a = [0, 1, 2, 3, 4, 5];
let mut iter = a.iter().step_by(2);

assert_eq!(iter.next(), Some(&0));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&4));
assert_eq!(iter.next(), None);
7
votes

As Dogbert said, use itertools' step.

You are going to be in a world of hurt if you can't use external crates. The Rust ecosystem strongly encourages everything to be pushed to crates. Maybe you should just clone the repository locally and use it that way if you can.

Otherwise, write it yourself:

use std::iter::Fuse;

struct Nth<I> {
    n: usize,
    iter: Fuse<I>,
}

impl<I> Iterator for Nth<I>
    where I: Iterator
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let mut nth = None;

        for _ in 0..self.n {
            nth = self.iter.next();
        }

        nth
    }
}

trait EveryNth: Iterator + Sized {
    fn every_nth(self, n: usize) -> Nth<Self> {
        Nth { n: n, iter: self.fuse() }
    }
}

impl<I> EveryNth for I where I: Iterator {}

fn main() {
    let x = [1,2,3,4,5,6,7,8,9];

    for v in x.iter().every_nth(1) { println!("{}", v) }
    println!("---");
    for v in x.iter().every_nth(2) { println!("{}", v) }
    println!("---");
    for v in x.iter().every_nth(3) { println!("{}", v) }
    println!("---");
    for v in x.iter().every_nth(4) { println!("{}", v) }
    println!("---");
    for v in x.iter().every_nth(5) { println!("{}", v) }
    println!("---");
    for v in x.iter().every_nth(6) { println!("{}", v) }
}

Note this is different behavior from itertools. You didn't specify where in the cycle of N the iterator picks from, so I chose the one that was easiest to code.