如何在 Rust 中对向量中的连续整数进行分组?

Ser*_*lev 4 grouping rust

我有一个Vec<i64>,我想知道所有连续的整数组。举个例子:

let v = vec![1, 2, 3, 5, 6, 7, 9, 10];
Run Code Online (Sandbox Code Playgroud)

我期待这样或类似的事情:

[[1, 2, 3], [5, 6, 7], [9, 10]];
Run Code Online (Sandbox Code Playgroud)

视图(向量的向量或者元组或其他东西)真的无关紧要,但我应该得到几个具有连续数字的分组列表。

乍一看,似乎我需要使用 itertools 和该group_by函数,但我不知道如何...

小智 6

let v = vec![1, 2, 3, 5, 6, 7, 9, 10];
let mut res = Vec::new();
let mut prev = v[0];
let mut sub_v = Vec::new();

sub_v.push(prev);

for i in 1..v.len() {
    if v[i] == prev + 1 {
        sub_v.push(v[i]);
        prev = v[i];
    } else {
        res.push(sub_v.clone());
        sub_v.clear();
        sub_v.push(v[i]);
        prev = v[i];
    }
}

res.push(sub_v);
Run Code Online (Sandbox Code Playgroud)

这应该可以解决你的问题。

迭代给定的向量,检查当前i64(在我的例子中i32)是否比前一个+1 i64,如果是,则将其推入向量(sub_v)。系列中断后,将 推sub_v入结果向量。重复。

但我猜你想要一些实用的东西?

  • 如果起始向量为空,这将发生恐慌。您可以使用“mem::replace”而不是克隆和清除​​向量。 (2认同)

tre*_*tcl 5

确实可以group_by用于此目的,但您可能并不真正想要。这是我可能会写的:

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
}
Run Code Online (Sandbox Code Playgroud)

这在原则上类似于 eXodiquas 的答案,但Vec<Vec<i64>>我没有累积 a ,而是使用索引来累积 aVec引用原始数据的切片引用。(这个问题解释了我为什么consecutive_slices要 take &[T]。)

Vec通过返回一个迭代器,也可以在不分配 a 的情况下做同样的事情;不过,我更喜欢上面的版本。这是我想出的零分配版本:

fn consecutive_slices(data: &[i64]) -> impl Iterator<Item = &[i64]> {
    let mut slice_start = 0;
    (1..data.len() + 1).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
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

它不像for循环那样可读,但它不需要Vec为返回值分配一个,所以这个版本更灵活。


这是一个“功能更强大”的版本,使用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()
}
Run Code Online (Sandbox Code Playgroud)

这个想法是制作一个关键函数,用于group_by获取每个元素与其在切片中的索引之间的差异。连续元素将具有相同的键,因为索引每次增加 1。我不喜欢这个版本的原因之一是很难获得原始数据结构的切片;您几乎必须创建一个Vec<Vec<i64>>(因此是两个collect)。另一个原因是我觉得读起来更难。

但是,当我第一次编写我喜欢的版本(第一个,带有for循环)时,它有一个错误(现已修复),而其他两个版本从一开始就是正确的。因此,即使对可读性和/或性能有一些影响,编写具有功能抽象的更密集代码也可能是有益的。