How to transpose a vector of vectors in Rust?
Asked Answered
M

3

12

So I have a Vec<Vec<T>> where the first vector groups by hours of day and the inner vector by days of week. I would like to transpose somehow the vectors to have it first by days then by hours. Is there a simple way to do it in Rust?

EDIT: I mean, I know how to do it with 2 for loops but is there a smarter/shorter way to do it functionally

Monecious answered 23/10, 2020 at 10:49 Comment(2)
Please explain more clearly "the first vector groups by hours of day and the inner vector by days of week". What is T, maybe a DateTime? Can you show an example of input with the desired output?Tragic
It doesn't matter. Let's say it's T as in the answerMonecious
L
20

You can use some iterators:

fn transpose<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>>
where
    T: Clone,
{
    assert!(!v.is_empty());
    (0..v[0].len())
        .map(|i| v.iter().map(|inner| inner[i].clone()).collect::<Vec<T>>())
        .collect()
}

As user4815162342 comments, here is a version without Clone:

fn transpose2<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>> {
    assert!(!v.is_empty());
    let len = v[0].len();
    let mut iters: Vec<_> = v.into_iter().map(|n| n.into_iter()).collect();
    (0..len)
        .map(|_| {
            iters
                .iter_mut()
                .map(|n| n.next().unwrap())
                .collect::<Vec<T>>()
        })
        .collect()
}

Playground

Lunge answered 23/10, 2020 at 11:34 Comment(3)
Since the number of values is the same, it would be nice to have a version that doesn't require T: Clone - perhaps by draining the inner vectors.Pipistrelle
Nice edit! I've come up with this idea that first reverses the inner vectors and then pops them off the end, but a vector of into_iters is much nicer, and faster.Pipistrelle
Does this functionality exist in a popular library somewhere?Shields
C
1

The question says "I know how to do it with 2 for loops but is there a smarter/shorter way to do it functionally", however that it is probably the best answer for the title. Here's a two-for-loop solution that avoids T: Clone and avoids allocating a scratch Vec for iterators:

fn transpose<T>(original: Vec<Vec<T>>) -> Vec<Vec<T>> {
    assert!(!original.is_empty());
    let mut transposed = (0..original[0].len()).map(|_| vec![]).collect::<Vec<_>>();

    for original_row in original {
        for (item, transposed_row) in original_row.into_iter().zip(&mut transposed) {
            transposed_row.push(item);
        }
    }

    transposed
}

Someone could probably make it more "functional" than I, but this is already a bit difficult to read, try as I might.

Cateran answered 14/12, 2022 at 2:44 Comment(0)
W
1

Here's one way

let v = vec![vec![1,2,3,4], vec![5,6,7,8]];
let rows = v.len();
let cols = v[0].len();

let transposed: Vec<Vec<_>> = (0..cols).map(|col| {
    (0..rows)
        .map(|row| v[row][col])
        .collect()
}).collect();
Wilsey answered 15/3, 2023 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.