Mark Fisher
Mark Fisher

Reputation: 9886

How do I chain operators over lists in rust? Looking for equivalent to kotlin code

I have the following code in kotlin and I'm trying to find a rust equivalent, but don't understand the chaining mechanism in rust to convert.

val windowSize = 2
val result = listOf(1, 2, 3, 4, 5, 6)
  .windowed(windowSize, 1) ; [[1,2], [2,3], [3,4], [4,5], [5,6]]
  .map { it.sum() }        ; [  3,     5,     7,     9,     11]
  .windowed(2, 1)          ; [[3,5], [5,7], [7,9], [9,11] ]
  .count { it[0] < it[1] } ; 4

;; result = 4, as there are 4 sequences that have first number less than 2nd,
;; when considering a sliding window over the original data of 2 items at a time.

It just takes a list of integers, splits them into pairs (but the windowSize will be a function parameter), sums those groups, splits the sums into pairs again, and finds where each second element is bigger than the previous, so finding increasing values over moving windows.

I'm converting this to the rust equivalent, but struggling to understand how to chain operations together.

What I've got so far is:

let input = [1, 2, 3, 4, 5, 6];
input.windows(2)
    .map(|es| es.iter().sum())
    // what goes here to do the next windows(2) operation?
    .for_each(|x: u32| println!("{}", x));

I can "for_each" over the map to do things on the iteration, but I can't split it with another "windows()", or don't know the magic to make that possible. IntelliJ is showing me the return type from map is impl Iterator<Item=?>

Can anyone enlighten me please? I am an absolute beginner on rust, so this is undoubtedly to do with my understanding of the language as a whole.

Upvotes: 1

Views: 444

Answers (4)

Stargateur
Stargateur

Reputation: 26757

Using std and stable only:

fn main() {
    let input = [1i32, 2, 3, 4, 5, 6];
    let mut iter = input.windows(2).map(|es| es.iter().sum::<i32>());

    let n = if let Some(mut prev) = iter.next() {
        iter.map(|i| {
            let ret = (prev, i);
            prev = i;
            ret
        })
        .filter(|(a, b)| a < b)
        .count()
    } else {
        0
    };

    println!("{}", n);
}

This should be very fast.

Upvotes: 0

Jmb
Jmb

Reputation: 23434

As stated in @Aiden4's answer, the best solution is to use itertools::tuple_windows. It is however possible using just the standard library and without collecting to an intermediate vector using Iterator::scan:

fn main() {
    let input = [1i32, 2, 3, 4, 5, 6];
    let output: usize = input
        .windows(2)
        .map(|es| es.iter().sum())
        .scan(0, |prev, cur| {
            let res = (*prev, cur);
            *prev = cur;
            Some(res)
        })
        .skip(1)
        .filter(|(a, b)| a < b)
        .count();
    println!("{}", output);
}

Playground

Upvotes: 0

Aiden4
Aiden4

Reputation: 2664

The Itertools crate provides a reasonably convenient way to do this with the tuple_windows method.

use itertools::Itertools; 

fn main() {
    let input = [1i32, 2, 3, 4, 5, 6];
    let output: usize = input
        .windows(2)
        .map(|es| es.iter().sum::<i32>())
        .tuple_windows()
        .filter(|(a, b)| a < b)
        .count();
    println!("{}", output);
}

Playground

The standard library does not have a way to do this without collecting the iterator first, which requires two passes through the data.

Upvotes: 5

Netwave
Netwave

Reputation: 42756

It is a bit convoluted to chain everything. You need to collect into a vec so you can access windows again. Then you can flat_map the windows to array references (taken from this other answer) to complete what you want to do:

fn main() {
    let input = [1usize, 2, 3, 4, 5, 6];
    let res = input
        .windows(2)
        .map(|es| es.iter().sum::<usize>())
        .collect::<Vec<_>>()
        .windows(2)
        .flat_map(<[usize; 2]>::try_from)
        .filter(|[a, b]| a < b)
        .count();
    println!("{}", res);
}

Playground


Note: Nightly feature array_windows that use const generic allow to remove the .flat_map(<&[usize; 2]>::try_from) call

Upvotes: 1

Related Questions