All possible permutations of 2 vectors on rust - rust

Good afternoon. I have 2 vectors of unequal length
let names = vec!["Marie", "Jana", "Eva", "Anna"];
let surnames = vec!["Novakova", "Svobodova", "Novotna", "Dvorakova", "Kralova"];
How can I go through all the possible combinations of first and last names ?
Conditional 3 vector I want to get:
let gen_full_name = vec!["Marie Novakova", "Jana Novakova", "Eva Novotna", "Anna Novotna", "Jana Novakova", ...];

let names = vec!["Marie", "Jana", "Eva", "Anna"];
let surnames = vec!["Novakova", "Svobodova", "Novotna", "Dvorakova", "Kralova"];
let mut full_names = Vec::with_capacity(names.len() * surnames.len());
for name in &names {
for surname in &surnames {
full_names.push(format!("{} {}", name, surname));
}
}
println!("{:?}", full_names);
No unnecessary allocations required.

This would be a manual version:
let names = vec!["Marie", "Jana", "Eva", "Anna"];
let surnames = vec!["Novakova", "Svobodova", "Novotna", "Dvorakova", "Kralova"];
let combinations = names.iter()
.map(|&n|
surnames.iter()
.map(move |&s| format!("{} {}", &n, &s))
.collect::<Vec<_>>())
.fold(vec![], |mut v, mut dat| { v.append(&mut dat); v} );

Related

How to sum elements of Vec<Vec<f64>> together into a Vec<f64>?

I am looking for an "rusty" way to accumulate a Vec<Vec> into a Vec such that the 1st element of every inner Vec is summed together, every 2nd element of each Vec is summed together, etc..., and the results are collected into a Vec? If I just use sum(), fold(), or accumulate() I believe I will sum entire 1st Vec together into a single element, rather than the 1st element of each inner Vec contained in the 2D Vec.
pub fn main() {
let v1 = vec![1.1, 2.2, 3.3];
let vv = vec![v1; 3];
let desired_result = vec![3.3, 6.6, 9.9];
}
Sometimes it's easy to forget in Rust that the imperative approach exists and is an easy solution.
let mut sums = vec![0.0; vv[0].len()];
for v in vv {
for (i, x) in v.into_iter().enumerate() {
sums[i] += x;
}
}
While I prefer #orlp's solution, if you're hell-bent on doing this the most functionally possible, you could do it like this:
let v1 = vec![1.1, 2.2, 3.3];
let vv = vec![v1; 3];
let sums = vec![0.0; vv[0].len()];
let summed = vv.into_iter().fold(sums, |mut sums, v| {
v.into_iter().enumerate().for_each(|(i, x)| sums[i] += x);
sums
});
Also if knowing beforehand the size of the inner vectors (or taking it from the first occurence in the vv vector), you can use a range iterator:
pub fn main() {
let v1 = vec![1.1, 2.2, 3.3];
let v1_len = v1.len();
let vv = vec![v1; 3];
let res: Vec<f64> = (0..v1_len)
.map(|i| vv.iter().map(|v| v.get(i).unwrap()).sum())
.collect();
println!("{res:?}");
}
Playground

How do I get the key associated with the maximum value of a Rust HashMap?

For example, given the data:
2 : 4
1 : 3
5 : 2
The function would return 2 since its value (4) is the highest.
I am doing:
let mut max_val = 0;
let mut max_key = "";
for (k, v) in a_hash_map.iter() {
if *v > max_val {
max_key = k;
max_val = *v;
}
}
Is there a nicer or quicker or simpler way to do this?
Iterate through all the key-value pairs in the hashmap, comparing them by the values, keeping only the key of the maximum:
use std::collections::HashMap;
fn example<K, V>(a_hash_map: &HashMap<K, V>) -> Option<&K>
where
V: Ord,
{
a_hash_map
.iter()
.max_by(|a, b| a.1.cmp(&b.1))
.map(|(k, _v)| k)
}
fn main() {
let map: HashMap<_, _> = vec![(2, 4), (1, 3), (5, 2)].into_iter().collect();
dbg!(example(&map));
}
See also:
How do I create a map from a list in a functional way?
How can min_by_key or max_by_key be used with references to a value created during iteration?
let key_with_max_value = a_hashmap.iter().max_by_key(|entry | entry.1).unwrap();
dbg!(key_with_max_value.0);
You will need to do better error handling. This code just does an unwrap, expecting that there would be at least one element.
perhaps you can have a try with this: if let Some(max) = a_hash_map.keys().max(){println!("max:{}", max);}

How do I delimit a string based on whitespace (tabs or uneven space) into a tuple?

I want to read a whitespace-separated file (could be tab or uneven space) into tuples:
use std::io::{BufReader, BufRead, Cursor};
fn main() {
let data = "
A 1 Pass
B 2 Fail
C 3 Fail
";
let lines = BufReader::new(Cursor::new(data))
.lines();
for line in lines {
let line_temp = line.unwrap();
let broken_line: Vec<&str> = line_temp.split(" ").collect(); // This works
// I want something like below:
// let (a, b, c) = ("A", 1, "Pass");
println!("{:?}", broken_line);
}
}
I want a to store the first column, b to store second column and so on.
a = A, b = 1, c = Pass
a = B, b = 2, c = Fail
// ...
Assuming your data is well-formed (aside from empty lines) and you don't have to worry about validating each individual line then you can do this
fn main() {
let data = "
A 1 Pass
B 2 Fail
C 3 Fail
";
for line in data.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
let mut parts = line.split_whitespace();
let tuple = (
parts.next().unwrap(),
parts.next().unwrap().parse::<i32>().unwrap(),
parts.next().unwrap(),
);
println!("{:?}", tuple);
}
}
playground

How do I reuse the SplitWhitespace iterator?

I've got a piece of code which is supposed to check if two sentences are "too similar", as defined by a heuristic made clearest by the code.
fn too_similar(thing1: &String, thing2: &String) -> bool {
let split1 = thing1.split_whitespace();
let split2 = thing2.split_whitespace();
let mut matches = 0;
for s1 in split1 {
for s2 in split2 {
if s1.eq(s2) {
matches = matches + 1;
break;
}
}
}
let longer_length =
if thing1.len() > thing2.len() {
thing1.len()
} else {
thing2.len()
};
matches > longer_length / 2
}
However, I'm getting the following compilation error:
error[E0382]: use of moved value: `split2`
--> src/main.rs:7:19
|
7 | for s2 in split2 {
| ^^^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `split2` has type `std::str::SplitWhitespace<'_>`, which does not implement the `Copy` trait
I'm not sure why split2 is getting moved in the first place, but what's the Rust way of writing this function?
split2 is getting moved because iterating with for consumes the iterator and since the type does not implement Copy, Rust isn't copying it implicitly.
You can fix this by creating a new iterator inside the first for:
let split1 = thing1.split_whitespace();
let mut matches = 0;
for s1 in split1 {
for s2 in thing2.split_whitespace() {
if s1.eq(s2) {
matches = matches + 1;
break;
}
}
}
...
You can also rewrite the matches counting loop using some higher order functions available in the Iterator trait:
let matches = thing1.split_whitespace()
.flat_map(|c1| thing2.split_whitespace().filter(move |&c2| c1 == c2))
.count();
longer_length can also be written as:
let longer_length = std::cmp::max(thing1.len(), thing2.len());
There are possibly some better ways to do the word comparison.
If the phrases are long, then iterating over thing2's words for every word in thing1 is not very efficient. If you don't have to worry about words which appear more than once, then HashSet may help, and boils the iteration down to something like:
let words1: HashSet<&str> = thing1.split_whitespace().collect();
let words2: HashSet<&str> = thing2.split_whitespace().collect();
let matches = words1.intersection(&words2).count();
If you do care about repeated words you probably need a HashMap, and something like:
let mut words_hash1: HashMap<&str, usize> = HashMap::new();
for word in thing1.split_whitespace() {
*words_hash1.entry(word).or_insert(0) += 1;
}
let matches2: usize = thing2.split_whitespace()
.map(|s| words_hash1.get(s).cloned().unwrap_or(0))
.sum();

How can I randomly select one element from a vector or array?

I have a vector where the element is a (String, String). How can I randomly pick one of these elements?
You want the rand crate, specifically the choose method.
use rand::seq::SliceRandom; // 0.7.2
fn main() {
let vs = vec![0, 1, 2, 3, 4];
println!("{:?}", vs.choose(&mut rand::thread_rng()));
}
Using choose_multiple:
use rand::seq::SliceRandom; // 0.7.2
fn main() {
let samples = vec!["hi", "this", "is", "a", "test!"];
let sample: Vec<_> = samples
.choose_multiple(&mut rand::thread_rng(), 1)
.collect();
println!("{:?}", sample);
}
Another choice for weighted sampling that is already included in the rand crate is WeightedIndex, which has an example:
use rand::prelude::*;
use rand::distributions::WeightedIndex;
let choices = ['a', 'b', 'c'];
let weights = [2, 1, 1];
let dist = WeightedIndex::new(&weights).unwrap();
let mut rng = thread_rng();
for _ in 0..100 {
// 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c'
println!("{}", choices[dist.sample(&mut rng)]);
}
let items = [('a', 0), ('b', 3), ('c', 7)];
let dist2 = WeightedIndex::new(items.iter().map(|item| item.1)).unwrap();
for _ in 0..100 {
// 0% chance to print 'a', 30% chance to print 'b', 70% chance to print 'c'
println!("{}", items[dist2.sample(&mut rng)].0);
}
If you want to choose more than one element then the random_choice crate may be right for you:
extern crate random_choice;
use self::random_choice::random_choice;
fn main() {
let mut samples = vec!["hi", "this", "is", "a", "test!"];
let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
let number_choices = 100;
let choices = random_choice().random_choice_f64(&samples, &weights, number_choices);
for choice in choices {
print!("{}, ", choice);
}
}
If you also want to remove the chosen element, here's one way to do that (using the rand crate):
let mut vec = vec![0,1,2,3,4,5,6,7,8,9];
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
let value = vec.remove( index );
println!("index: {} value: {}", index, value);
println!("{:?}", vec);
Rust Playground
remove(index) removes that value at index (shifting all the elements after it to the left) and the returns the value that was at index (docs).
Another way of getting a random value is via the indexing method using rng.gen_range and vec.get(). This also prevents the borrowing of the value (which occurs with the vec.choose() method)
fn main() {
let mut rng = rand::thread_rng();
let my_strings : Vec<&str> = vec!["a", "b", "c"];
let random_string_index: usize = rng.gen_range(0..my_strings.len());
let string = my_strings[random_string_index];
println!("{:?}", string);
}

Resources