Reputation: 4892
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
Upvotes: 15
Views: 11804
Reputation: 11
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();
Upvotes: 1
Reputation: 60762
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.
Upvotes: 1
Reputation: 42796
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()
}
Upvotes: 23