You can indeed use group_by
for this, but you might not really want to. Here's what I would probably write instead:
fn consecutive_slices(data: &[i64]) -> Vec<&[i64]> {
let mut slice_start = 0;
let mut result = Vec::new();
for i in 1..data.len() {
if data[i - 1] + 1 != data[i] {
result.push(&data[slice_start..i]);
slice_start = i;
}
}
if data.len() > 0 {
result.push(&data[slice_start..]);
}
result
}
This is similar in principle to eXodiquas' answer, but instead of accumulating a Vec<Vec<i64>>
, I use the indices to accumulate a Vec
of slice references that refer to the original data. (This question explains why I made consecutive_slices
take &[T]
.)
It's also possible to do the same thing without allocating a Vec
, by returning an iterator; however, I like the above version better. Here's the zero-allocation version I came up with:
fn consecutive_slices(data: &[i64]) -> impl Iterator<Item = &[i64]> {
let mut slice_start = 0;
(1..=data.len()).flat_map(move |i| {
if i == data.len() || data[i - 1] + 1 != data[i] {
let begin = slice_start;
slice_start = i;
Some(&data[begin..i])
} else {
None
}
})
}
It's not as readable as a for
loop, but it doesn't need to allocate a Vec
for the return value, so this version is more flexible.
Here's a "more functional" version using group_by
:
use itertools::Itertools;
fn consecutive_slices(data: &[i64]) -> Vec<Vec<i64>> {
(&(0..data.len()).group_by(|&i| data[i] as usize - i))
.into_iter()
.map(|(_, group)| group.map(|i| data[i]).collect())
.collect()
}
The idea is to make a key function for group_by
that takes the difference between each element and its index in the slice. Consecutive elements will have the same key because indices increase by 1 each time. One reason I don't like this version is that it's quite difficult to get slices of the original data structure; you almost have to create a Vec<Vec<i64>>
(hence the two collect
s). The other reason is that I find it harder to read.
However, when I first wrote my preferred version (the first one, with the for
loop), it had a bug (now fixed), while the other two versions were correct from the start. So there may be merit to writing denser code with functional abstractions, even if there is some hit to readability and/or performance.
group_normal_data = [list(map(itemgetter(1), g)) for (k, g) in groupby(enumerate(source_normal), lambda ix: ix[0] - ix[1])]
. It's hard to understand of course, but it works just in only one line and I tried to make the same in Rust – Leandraenumerate
. But just like with list comprehensions in Python, it's easy to go crazy with iterator adapters until you have an unreadable mess. – Blanc