Reputation: 9886
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
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
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);
}
Upvotes: 0
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);
}
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
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);
}
Note: Nightly feature array_windows
that use const generic allow to remove the .flat_map(<&[usize; 2]>::try_from)
call
Upvotes: 1