I'm a newbie rust programmer and I'm writing a program that prints a table, and using a loop, gets the numeric values from the user and each time the user enters the value, the table gets updated and printed again until all the values are received.
class1 class2 class3 class4 class5
Sensor 0 0 0 0 0
visual 0 0 0 0 0
I want to put the cursor at the desired cell (and blink if possible) which the user is going to enter the value for. Which means at first iteration the cursor should be at the cell sensor-class1, 2nd iteration at the sensor-class2 and so on.
I searched for a while and seems that termion is the solution, but print!("{}", termion::cursor::Goto((10 * i).try_into().unwrap(), j));
shifts the printed table in each iteration and not the user input cursor:
extern crate termion;
use std::io;
fn main() {
let mut sensor_data = [0; 5];
let mut visual_data = [0; 5];
for j in 1..3 {
for i in 1..6 {
print!("{}", termion::clear::All);
print!("{}", termion::cursor::Goto(1, 1));
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"", "class1", "class2", "class3", "class4", "class5"
);
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"Sensor",
sensor_data[0],
sensor_data[1],
sensor_data[2],
sensor_data[3],
sensor_data[4]
);
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"visual",
visual_data[0],
visual_data[1],
visual_data[2],
visual_data[3],
visual_data[4]
);
//update the cursor position for user input
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
if j == 1 {sensor_data[i - 1] = input.trim().parse().expect("Please type a number!");}
else {visual_data[i - 1] = input.trim().parse().expect("Please type a number!");}
}
}
// print the complete table and go on
}
I think primarily, you are missing a stdout().flush().
print!(), contrary to println!(), does not write a newline character. On most systems a newline character causes a flush, but as there is no newline character, the Goto gets stuck in the write buffer.
An easy fix would be to flush() manually, as shown in the code sample below.
The more proper way, though, would be to access the terminal in raw mode to disable the buffer in the first place.
use std::io::{self, Write};
fn main() {
let mut sensor_data = [0; 5];
let mut visual_data = [0; 5];
for j in 1..3 {
for i in 1..6 {
print!("{}", termion::clear::All);
print!("{}", termion::cursor::Goto(1, 1));
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"", "class1", "class2", "class3", "class4", "class5"
);
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"Sensor",
sensor_data[0],
sensor_data[1],
sensor_data[2],
sensor_data[3],
sensor_data[4]
);
println!(
"{0: <10} {1: <10} {2: <10} {3: <10} {4: <10} {5: <10}",
"visual",
visual_data[0],
visual_data[1],
visual_data[2],
visual_data[3],
visual_data[4]
);
//update the cursor position for user input
print!("{}", termion::cursor::Goto(12 * i + 1, j + 1));
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
if j == 1 {
sensor_data[i as usize - 1] = input.trim().parse().expect("Please type a number!");
} else {
visual_data[i as usize - 1] = input.trim().parse().expect("Please type a number!");
}
}
}
// print the complete table and go on
}
Other minor fixes I added:
Remove extern crate as it is no longer required or advised
Change type of i to u16 and convert to usize instead of the other way round, to avoid the unwrap()
Related
I have a BTreeSet object and I want to convert it into a BTreeMap with default indices (i.e., starting from 0 and incrementing). More precisely, given this:
let set = BTreeSet::from([0, 2, 4, 6, 8]);
I want to end up with BTreeMap<usize, usize> that holds [(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)]. I can do it with a loop for example as
let mut tree = BTreeMap::new();
for (i, v) in set.clone().into_iter().enumerate() {
tree.insert(i, v);
}
But is there any shortcut (i.e., one line code) that does the same?
But is there any shortcut (i.e., one line code) that does the same?
let map = set.iter().copied().enumerate().collect::<BTreeMap<_, _>>();
println!("{:?}", map);
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8}
tho note that this is a BTreeMap<usize, i32>, because i32 is the default resolution for {integer} literals if they're otherwise unconstrained, which is the case here.
I have two approaches to grouping characters by the number of occurrences in a string. One of them is using std::collections::HashMap and the second one is using itertools::Itertools::group_by. Unfortunately, grouping with Itertools gives me undesirable results.
Example input word: "Barbara"
Using std::collections::HashMap
let map1 = word.to_lowercase()
.chars()
.fold(HashMap::new(), |mut acc, c| {
*acc.entry(c).or_insert(0) += 1;
acc
});
Result {'a': 3, 'b': 2, 'r': 2}
And using itertools::Itertools::group_by
let map2: HashMap<char, u32> = word.to_lowercase()
.chars()
.group_by(|&x| x)
.into_iter()
.map(|(k, v)| (k, v.count() as u32))
.collect();
Result {'r': 1, 'a': 1, 'b': 1}
Oddly enough, when the input string has identical characters in succession, Itertools takes those characters into account.
The question is, what makes it return different results?
Playground
You're looking for into_group_map_by. group_by only groups consecutive elements according to the docs.
use itertools::Itertools;
use std::collections::HashMap;
fn main() {
let word = "Barbara";
let map1 = word
.to_lowercase()
.chars()
.fold(HashMap::new(), |mut acc, c| {
*acc.entry(c).or_insert(0) += 1;
acc
});
println!("{:?}", map1);
let map2: HashMap<char, u32> = word
.to_lowercase()
.chars()
.into_group_map_by(|&x| x)
.into_iter()
.map(|(k, v)| (k, v.len() as u32))
.collect();
println!("{:?}", map2);
}
Output:
{'b': 2, 'a': 3, 'r': 2}
{'b': 2, 'r': 2, 'a': 3}
Playground
There's also into_grouping_map_by, which can be used for this like:
let map2: HashMap<char, u32> = word
.to_lowercase()
.chars()
.into_grouping_map_by(|&x| x)
.fold(0, |acc, _key, _value| acc + 1);
The documentation says (emphasis added):
fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
where
Self: Sized,
F: FnMut(&Self::Item) -> K,
K: PartialEq,
Return an iterable that can group iterator elements. Consecutive elements that map to the same key (“runs”), are assigned to the same group.
It only groups consecutive elements. You'll need to sort the characters before calling group_by.
let map2: HashMap<char, u32> = word.to_lowercase()
.chars()
.sorted()
.group_by(|&x| x)
...
Output:
{'a': 3, 'r': 2, 'b': 2}
{'b': 2, 'a': 3, 'r': 2}
Playground
In python its done this way:
>>> x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
How to sort a HashMap by values in rust?
My code so far:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let highest = count.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();
println!("largest hash: {:?}", highest); // largest hash: ("C", 11)
}
Unlike Python's dict, Rust's "built-in" hashmap is not ordered, so sorting it has no effect.
If you need an ordered map for some reason, you should use indexmap. Alternatively, BTreeMap is sorted based on the key.
As you don't really present any sort of compelling use case it's hard to provide counsel though.
Ya, sorted it by converting to vector:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let mut hash_vec: Vec<(&String, &u32)> = count.iter().collect();
println!("{:?}", hash_vec);
hash_vec.sort_by(|a, b| b.1.cmp(a.1));
println!("Sorted: {:?}", hash_vec); //Sorted: [("C", 11), ("D", 10), ("A", 5), ("B", 2)]
}
Sort HashMap data by value
I have a vector and I want to sort it, where the first criterion is frequency. Second criterion is position in the vector. If two elements have the same number of occurrences, I want the most recently seen element to take advantage and go first. In the end, I want to remove duplicate elements from it.
For instance, if the input is this:
fn main() {
let history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
}
The output should be:
3 4 5 2 6
How can I do this in Rust?
A straightforward method is to build hash maps for frequencies and positions of the elements:
use std::collections::HashMap;
fn frequency_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for &n in nums {
*map.entry(n).or_insert(0) += 1;
}
map
}
fn position_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for (pos, &n) in nums.iter().enumerate() {
map.insert(n, pos);
}
map
}
And then do an unstable sort by position followed by a stable sort by frequency:
fn custom_sort(nums: &mut Vec<i32>) {
let freq_map = frequency_map(nums);
let pos_map = position_map(nums);
nums.sort_unstable_by(|a, b| pos_map.get(b).unwrap().cmp(pos_map.get(a).unwrap()));
nums.dedup();
nums.sort_by(|a, b| freq_map.get(b).unwrap().cmp(freq_map.get(a).unwrap()));
}
Example:
use itertools::Itertools;
fn main() {
let mut history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
custom_sort(&mut history);
println!("[{}]", history.iter().format(", "));
}
Output:
[3, 4, 5, 2, 6]
(playground)
I am trying to check if two Matrix Market format files contain the same matrix. In my actual code, due to the use of multi-threading, there is no guarantee that I am inserting items into a TriMat in the same order before being serialized to disk. As a result, when I load the resulting files and compare them, they are not always the same. How can I compare two different .mtx files and ensure that they are the same, regardless of insertion order?
Example code:
extern crate sprs;
use sprs::io::{write_matrix_market, read_matrix_market};
use sprs::TriMat;
fn main() {
let mut mat = TriMat::new((4, 20));
let mut vals = Vec::new();
vals.push((0, 19, 1));
vals.push((1, 14, 1));
vals.push((1, 19, 1));
vals.push((2, 17, 2));
for (i, j, v) in vals {
mat.add_triplet(i, j, v)
}
let _ = write_matrix_market("a.mtx", &mat).unwrap();
let mut mat2 = TriMat::new((4, 20));
let mut vals2 = Vec::new();
vals2.push((0, 19, 1));
vals2.push((1, 14, 1));
vals2.push((2, 17, 2)); // different order
vals2.push((1, 19, 1));
for (i, j, v) in vals2 {
mat2.add_triplet(i, j, v)
}
let _ = write_matrix_market("b.mtx", &mat2).unwrap();
let seen_mat: TriMat<usize> = read_matrix_market("a.mtx").unwrap();
let expected_mat: TriMat<usize> = read_matrix_market("b.mtx").unwrap();
assert_eq!(seen_mat, expected_mat);
}
And the resulting error:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `TriMatBase { rows: 4, cols: 20, row_inds: [0, 1, 1, 2], col_inds: [19, 14, 19, 17], data: [1, 1, 1, 2] }`,
right: `TriMatBase { rows: 4, cols: 20, row_inds: [0, 1, 2, 1], col_inds: [19, 14, 17, 19], data: [1, 1, 2, 1] }`', src/main.rs:31:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
You can see that these two matrices are actually identical, but that the items have been inserted in different orders.
Turns out you can convert to CSR to get it to work:
let seen_mat: TriMat<usize> = read_matrix_market("a.mtx").unwrap();
let expected_mat: TriMat<usize> = read_matrix_market("b.mtx").unwrap();
let a = seen_mat.to_csr();
let b = expected_mat.to_csr();
assert_eq!(a, b);