Antonin GAVREL
Antonin GAVREL

Reputation: 11269

How can I split a vector into smaller vectors of size N?

How to split a vector

let v: Vec<u8>; // vector with size x

into a vector of vectors of maxsize n? Pseudocode:

let n: usize = 1024;
let chunks_list: Vec<Vec<u8>> = chunks(v, n);

or using slices (to avoid copying):

let v: &[u8]; 
let chunks_list: Vec<&[u8]> = chunks(v, n);

Upvotes: 20

Views: 18474

Answers (4)

Antonin GAVREL
Antonin GAVREL

Reputation: 11269

There is a method already existing for slices:

pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the beginning of the slice. The chunks are slices and do not overlap. If chunk_size does not divide the length of the slice, then the last chunk will not have length chunk_size.

There is also chunks_mut for mutability as well as chunks_exact and chunks_exact_mut if the last chunk has to respect the size n, along with the unsafe as_chunks_unchecked in case we assume there is no remainder, see below example:

fn main() {
    let v: [u8; 5] = *b"lorem";
    let n = 2;
    let chunks = v.chunks(n);
    let chunks_list: Vec<&[u8]> = chunks.collect();
    println!("{:?}", chunks_list);
}

Using a slice instead of vectors has some benefits, notably avoiding the overhead of copying.

Upvotes: 4

Shepmaster
Shepmaster

Reputation: 432189

If it's required to take a Vec and split it into multiple Vecs, I'd use Itertools::chunks. This takes an iterator and returns an iterator of iterators. You can then choose to collect both the inner and outer iterators into Vecs:

use itertools::Itertools; // 0.10.0

fn main() {
    let v = vec![String::from("A"), String::from("B"), String::from("C")];
    
    let x: Vec<Vec<String>> = v.into_iter().chunks(2).into_iter().map(|c| c.collect()).collect();
    
    eprintln!("{:?}", x);
}
[["A", "B"], ["C"]]

This has the benefit of taking ownership of each value in the original vector. No data needs to be copied, but it does need to be moved. If you can use slices instead, it's much better to use slice::chunks.

Upvotes: 2

Denys S&#233;guret
Denys S&#233;guret

Reputation: 382474

Rust slices already contain the necessary method for that: chunks.

Starting from this:

let src: Vec<u8> = vec![1, 2, 3, 4, 5];

you can get a vector of slices (no copy):

let dst: Vec<&[u8]> = src.chunks(3).collect();

or a vector of vectors (slower, heavier):

let dst: Vec<Vec<u8>> = src.chunks(3).map(|s| s.into()).collect();

playground

Upvotes: 27

Unapiedra
Unapiedra

Reputation: 16248

Here is one approach:

use std::{usize, vec};

fn chunks(data: Vec<u8>, chunk_size: usize) -> Vec<Vec<u8>> {
    let mut results = vec![];
    let mut current = vec![];
    for i in data {
        if current.len() >= chunk_size {
            results.push(current);
            current = vec![];
        }
        current.push(i);
    }
    results.push(current);

    return results;
}

fn main() {
    let v: Vec<u8> = (1..100).collect();
    let n: usize = 24;
    let chunks_list = chunks(v, n);

    println!("{:#?}", chunks_list);
}

Upvotes: 1

Related Questions