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

Time:12-02

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.

CodePudding user response:

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.

CodePudding user response:

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

  • Related