简体   繁体   中英

How can I skip the Nth element in a Rust iterator?

Iterators have a skip method that skips the first n elements:

let list = vec![1, 2, 3];
let iterator = list.iter();
let skip_iter = iterator.skip(2); //skip the first 2 elements

I could not find a method to skip only the n -th element in the iterator. Do I need to implement something on my own or is there a method somewhere I haven't found?

That seems to be a very specific operation. There is no adaptor for that in the standard library or the itertools crate.

It's easy to implement nonetheless. One could enumerate each element and filter on the index:

iter.enumerate().filter(|&(i, _)| i != n).map(|(_, v)| v)

Playground

I am partial to the filter_map version

fn main() {
    let v = vec![1, 2, 3];
    let n = 1;
    let x: Vec<_> = v.into_iter()
        .enumerate()
        .filter_map(|(i, e)| if i != n { Some(e) } else { None })
        .collect();
    println!("{:?}", x);
}

Playground

I already wanted to skip some range. The best in my opinion is to create an iterator:

mod skip_range {
    use std::ops::Range;
    use std::iter::Skip;

    /// Either the user provided iterator, or a `Skip` one.
    enum Either<I: Iterator> {
        Iter(I),
        Skip(Skip<I>),
    }

    pub struct SkipRange<I: Iterator> {
        it: Option<Either<I>>,
        count: usize,
        range: Range<usize>,
    }

    impl<I: Iterator> SkipRange<I> {
        pub fn new(it: I, range: Range<usize>) -> Self {
            SkipRange { it: Some(Either::Iter(it)), count: 0, range }
        }
    }

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

        fn next(&mut self) -> Option<Self::Item> {
            // If we are in the part we must skip, change the iterator to `Skip`
            if self.count == self.range.start {
                self.count = self.range.end;
                if let Some(Either::Iter(it)) = self.it.take() {
                    self.it = Some(Either::Skip(it.skip(self.range.end - self.range.start)));
                }
            } else {
                self.count += 1;
            }
            match &mut self.it {
                Some(Either::Iter(it)) => it.next(),
                Some(Either::Skip(it)) => it.next(),
                _ => unreachable!(),
            }
        }
    }
}

use skip_range::SkipRange;

fn main() {
    let v = vec![0, 1, 2, 3, 4, 5];
    let it = SkipRange::new(v.into_iter(), 2..4);

    let res: Vec<_> = it.collect();
    assert_eq!(res, vec![0, 1, 4, 5]);
}

The principle is to use 2 different iterators: the first one is given by the user, the second one is a Skip iterator, created from the first one.

If you have access to original collection, it could be

let items = ["a", "b", "c", "d"];
let skipped_2nd = items.iter().take(1).chain(items.iter().skip(2));

I don't think there is something in the stdlib, but here's yet another pretty simple way to go about it.

fn main() {
    let (v, idx) = (vec!["foo", "bar", "baz", "qux"], 2_usize);

    let skipped = v[..idx].iter().chain(v[idx + 1..].iter());
    skipped.for_each(|&val| {
        dbg!(val);
    });
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f47a28fd681fee2fe82b57c073d52648

More concise:

let iter = vs
            .iter()
            .enumerate()
            .filter_map(|(i, el)| (i == n).then(|| el));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM