rust macro, how to control the different vec have the same length? - rust

code first:
use std::collections::HashMap;
macro_rules! arr{
([$($t:expr=>[$($c:expr),*]),*]) => {
vec![
$({
let mut m = HashMap::new();
m.insert($t, vec![$($c),*]);
m
}),*
]
};
}
fn main() {
let a = arr!([
"A"=>[1,2,3],
"B"=>[3,4]
]);
println!("{:?}", a);
//print: [{"A": [1, 2, 3]}, {"B": [3, 4]}]
}
I have above macro to generate a vec, contains several HashMap, in which these HashMap value is a vec as well,
{"A": [1, 2, 3]} => vec value length: 3,
{"B": [3, 4]} => vec value length: 2,
I wanna all the HashMap have the same length,
how to write in the macro to control this?

You can change the macro so that it creates a block (second set of {} encapsulating the macro definition) that you can set helper variables in and do a second pass over your vector, resizing anything that is smaller than the largest array.
In this case I've resized the arrays with the default value of the type to keep it simple. You may wish to wrap the data in Some().
This:
use std::cmp;
use std::collections::HashMap;
use std::default::Default;
macro_rules! arr{
([$($t:expr=>[$($c:expr),*]),*]) => {{
let mut max = 0;
let mut result = vec![
$({
let mut m = HashMap::new();
m.insert($t, vec![$($c),*]);
// Simply unwrap here as we know we inserted at this key above
max = cmp::max(max, m.get($t).unwrap().len());
m
}),*
];
for m in result.iter_mut() {
for v in m.values_mut() {
if v.len() < max {
v.resize_with(max, Default::default);
}
}
}
result
}};
}
fn main() {
let a = arr!([
"A"=>[1,2,3],
"B"=>[3,4]
]);
println!("{:?}", a);
//print: [{"A": [1, 2, 3]}, {"B": [3, 4]}]
}
Yields:
[{"A": [1, 2, 3]}, {"B": [3, 4, 0]}]

Related

With a HashMap<T, HashMap<T, i32>>, how can I insert the inner HashMap by the outer key?

For example, if I have a vector [1, 2], and I want to build a HashMap like <1: <2: 0>>, how can I insert the key and value? (The 0 is a default number), you can use x[0], x[1] to describe the elements in the vector.
I try to use map.insert(k,v), however, I don't know how to write the second parameter.
If I understand your question correctly, you are trying to, given a vector [n, m], insert a (key, value) pair into a HashMap where the key is n and the value is another HashMap, containing the (key, value) pair, (m, 0). That is, in JSON:
{ n: { m: 0 } }
As one commenter mentioned, you'll need to create the second map.
use std::collections::HashMap;
// I have arbitrarily picked `u8` for `T` here.
let mut map: HashMap<u8, HashMap<u8, i32>> = HashMap::new();
// And here's the vector we're given, maybe as a function argument:
let v: Vec<u8> = vec![1, 2];
// So we start by constructing our sub-map:
let mut sub_map: HashMap<u8, i32> = HashMap::new();
sub_map.insert(v[1], 0);
// And then we insert it into our outer map:
map.insert(v[0], sub_map);
So this solution answers your question, but it's kind of a crummy one, because I can easily imagine that you will want to process any number of 2-vectors and generate a more complicated map; for example, perhaps you want to, given this set of inputs:
[1, 2], [1, 3], [2, 5], [3, 5]
arrive at this map:
{
1: { 2: 0, 3: 0 },
2: { 5: 0 },
3: { 5: 0 }
}
The above technique would have blown the { 2: 0 } out of the first inner value when inserting the { 3: 0 }. What we probably want instead is a kind of "update or insert" (I have heard database guys use the term "upsert").
So we want something like this:
map.entry(v[0]).or_default().insert(v[1], 0);
The .or_default() will insert an empty inner HashMap (the type returned by HashMap::default()) keyed to the value of v[0] if the outer HashMap doesn't yet have one (then the .insert(v[1], 0) inserts the values you want in the inner map).
So in action it might look something like this:
let v: Vec<Vec<u8>> = vec![
vec![1, 2],
vec![1, 3],
vec![2, 5],
vec![3, 5],
];
let mut map: HashMap<u8, HashMap<u8, i32>> = HashMap::new();
for u in v.iter() {
map.entry(u[0]).or_default().insert(u[1], 0);
}
println!("{:#?}", &map);
/* should produce something like
{
1: {
2: 0,
3: 0,
},
2: {
5: 0,
},
3: {
5: 0,
},
}
*/

How to create statically sized array in Rust?

I have the following array of constants in Rust:
const COUNTS: [usize; 5] = [1, 2, 4, 8, 16];
And the point is that I want to iterate over it like this:
for (i, count) in COUNTS.iter().enumerate() {
const length: usize = *count/2;
let indices: [usize; length] = Vec::from_iter(0..length).try_into().unwrap();
let set: IndexSet<usize> = IndexSet::from(indices);
...
}
The point is that from method of IndexSet requires a statically sized array, i.e., [T; N]. What's the proper way here to create a statically sized array that includes the half of the values? Because the above code throws an error at const length: usize = *count/2 that count is a non-constant.
You probably want to use .collect instead, which is a standard way to instantiate a collection (here IndexSet) from an other collection (here a range of numbers).
use indexmap::set::IndexSet;
const COUNTS: [usize; 5] = [1, 2, 4, 8, 16];
fn main() {
for (i, count) in COUNTS.iter().copied().enumerate() {
let length = count/2;
let set = (0..length).collect::<IndexSet<_>>();
}
}
See the playground.
You need to statically add the array size:
let indices: [usize; 5] = Vec::from_iter(0..length).try_into().unwrap();
Which invalidates the purpouse of your code. That said, IndexSet implements FromIterator:
use indexmap::IndexSet;
const COUNTS: [usize; 5] = [1, 2, 4, 8, 16];
fn main() {
for (i, count) in COUNTS.iter().enumerate() {
let length: usize = *count / 2;
let set: IndexSet<usize> = IndexSet::from_iter(0..length);
}
}
Playground

Insert into Rust array in place, push other elements down

I'm trying to do the following in Rust, specifically using arrays (I don't want to use vectors here, and want elements pushed out of the array if we're done).
let mut x = [1, 2, 3, 4, 5];
// array, number to insert, place to be inserted at
insert_in_place(&x, 7, 1);
// x is now [1, 7, 2, 3, 4];
How do you implement insert_in_place?
I think there's a way to do this using slices, but I'm still learning and wondering if there's a really elegant way to do this kind of thing.
fn insert_in_place<T>(array: &mut [T], value: T, index: usize) {
*array.last_mut().unwrap() = value;
array[index..].rotate_right(1);
}
Try it online!
Or equivalently:
fn insert_in_place<T>(array: &mut [T], value: T, index: usize) {
array[index..].rotate_right(1);
array[index] = value;
}
Try it online!
Iterate the slice, skipping elements before the index of the one you need to insert. Then swap each element with its previous element (or, for the first one, use the item to add).
fn insert_in_place<T>(x: &mut [T], new: T, index: usize) {
let mut next = new;
for e in x.iter_mut().skip(index) {
std::mem::swap(e, &mut next);
}
}
fn main() {
let mut x = [1, 2, 3, 4, 5];
// array, number to insert, place to be inserted at
insert_in_place(&mut x, 7, 1);
// x is now [1, 7, 2, 3, 4];
println!("{:?}", x);
}

How to idiomatically check if a 2-dimensional vector is a table? [duplicate]

Given a vector of vectors of some value T, ie. Vec<Vec<T>>.
What's the idiomatic way to check if the inner vectors have the same length? (without external dependencies)
That is, true if all the inner vectors have the same length, and false otherwise.
You can use the all method to check if all elements of an iterator match a predicate. Then just compare against the first element in the list.
fn main() {
let vec_of_vecs = vec![
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3, 4], // remove this to prove that it works for both cases
];
let all_same_length = vec_of_vecs
.iter()
.all(|ref v| v.len() == vec_of_vecs[0].len());
if all_same_length {
println!("They're all the same");
} else {
println!("They are not the same");
}
}
An other solution more generic and idiomatic in my opinion:
fn all_eq_len<'a, T, E: 'a>(collection: T) -> bool
where
T: IntoIterator<Item = &'a Vec<E>>,
{
let mut iter = collection.into_iter();
if let Some(first) = iter.next() {
let len = first.len();
iter.all(|v| v.len() == len)
} else {
true
}
}
And of course using itertools:
use itertools::Itertools;
vec_of_vecs.iter().map(|v| v.len()).all_equal()

Check if length of all vectors is the same in Rust

Given a vector of vectors of some value T, ie. Vec<Vec<T>>.
What's the idiomatic way to check if the inner vectors have the same length? (without external dependencies)
That is, true if all the inner vectors have the same length, and false otherwise.
You can use the all method to check if all elements of an iterator match a predicate. Then just compare against the first element in the list.
fn main() {
let vec_of_vecs = vec![
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3],
vec![1, 2, 3, 4], // remove this to prove that it works for both cases
];
let all_same_length = vec_of_vecs
.iter()
.all(|ref v| v.len() == vec_of_vecs[0].len());
if all_same_length {
println!("They're all the same");
} else {
println!("They are not the same");
}
}
An other solution more generic and idiomatic in my opinion:
fn all_eq_len<'a, T, E: 'a>(collection: T) -> bool
where
T: IntoIterator<Item = &'a Vec<E>>,
{
let mut iter = collection.into_iter();
if let Some(first) = iter.next() {
let len = first.len();
iter.all(|v| v.len() == len)
} else {
true
}
}
And of course using itertools:
use itertools::Itertools;
vec_of_vecs.iter().map(|v| v.len()).all_equal()

Resources