18

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
eisterman
  • 486
  • 1
  • 5
  • 13
  • 1
    I don't think there exists a specific method to do that, but you could `enumerate().filter(|(i, v)| (i + 1) != n).map(|(i, v)| v)` to skip the `n`th element. – EvilTak Mar 14 '18 at 10:43

6 Answers6

23

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

E_net4
  • 27,810
  • 13
  • 101
  • 139
16

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

user25064
  • 2,020
  • 2
  • 16
  • 28
  • What does this buy over `.filter(..)`? It seems longer for no gain. – Centril Mar 14 '18 at 16:21
  • 3
    The solution that just uses `filter` returns tuples including the index, this returns the underlying elements without the index. It depends on what outcome you want – user25064 Mar 14 '18 at 16:40
  • The `filter_map` closure can be even more terse as of Rust 1.50, if you like: `|(i, e)| (i != n).then(|| e)` – Orez May 17 '22 at 16:44
3

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));
Petr Gladkikh
  • 1,906
  • 2
  • 18
  • 31
2

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.

Boiethios
  • 38,438
  • 19
  • 134
  • 183
0

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

n8henrie
  • 2,737
  • 3
  • 29
  • 45
  • that only work for vector, `for_each()` is not rusty. – Stargateur May 21 '21 at 21:52
  • I don't think that's strictly correct, it should work fine for a variety of types that are `Index> + Index>`, right? For example it seems to work fine if I change `v` to a `[&'static str; 4]` or `&[&'static str; 4]` instead of `Vec`. EDIT: typo – n8henrie May 21 '21 at 22:13
  • Also, I've never heard anyone else complain about `for_each`. Is that just your opinion, or has it been formally disparaged elsewhere? – n8henrie May 21 '21 at 22:15
  • vector is fundamentally a slice. for your last message, both, it's my opinion, and it accepted use for_each() is not great, cause iterator should not have side effect (or at least avoid it) there is no reason to prefer for_each() when there is the for loop of rust that make it clear is nicely formatted. – Stargateur May 21 '21 at 23:05
  • Correct. Additionally, `Vec` is what the OP used in their example, and since the use case in question requires a sequence (should maintain order for a skip to make sense), I don't see why you took issue with me using that in my answer. https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.for_each specifically references `chain` as being a case in which `for_each` may have an advantage, so it seems like there may be *some* reason to prefer it. Right? EDIT: typo – n8henrie May 21 '21 at 23:41
  • I wonder if this optimization work, you get the point, but still question is about iterator, vector is just here to have a simple iterator example. – Stargateur May 22 '21 at 01:13
0

More concise:

let iter = vs
            .iter()
            .enumerate()
            .filter_map(|(i, el)| (i == n).then(|| el));
Nearoo
  • 4,454
  • 3
  • 28
  • 39