Initial commit with my view on how to answer the exercices
This commit is contained in:
commit
9059b76493
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
4
Cargo.lock
generated
Normal file
4
Cargo.lock
generated
Normal file
@ -0,0 +1,4 @@
|
||||
[[package]]
|
||||
name = "chapter8_exercices"
|
||||
version = "0.1.0"
|
||||
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "chapter8_exercices"
|
||||
version = "0.1.0"
|
||||
authors = ["Zykino <Zykino@users.noreply.github.com>"]
|
||||
|
||||
[dependencies]
|
||||
# regex = "1"
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# My take Rust second edition chapter 8 exercices
|
||||
|
||||
> Given a list of integers, use a vector and return the mean (the average value), median (when sorted, the value in the middle position), and mode (the value that occurs most often; a hash map will be helpful here) of the list.
|
||||
>
|
||||
> Convert strings to pig latin. The first consonant of each word is moved to the end of the word and “ay” is added, so “first” becomes “irst-fay.” Words that start with a vowel have “hay” added to the end instead (“apple” becomes “apple-hay”). Keep in mind the details about UTF-8 encoding!
|
||||
>
|
||||
> Using a hash map and vectors, create a text interface to allow a user to add employee names to a department in a company. For example, “Add Sally to Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all people in a department or all people in the company by department, sorted alphabetically.
|
||||
|
||||
Any feedback to make it more idiomatic Rust and/or have better API is welcome.
|
||||
I sometimes use the `showroom` functions to keep all the code (lib + tests) in the same file.
|
179
src/company.rs
Normal file
179
src/company.rs
Normal file
@ -0,0 +1,179 @@
|
||||
// extern crate regex;
|
||||
//
|
||||
// use self::regex::Regex;
|
||||
use std::str::FromStr;
|
||||
use std::str::SplitWhitespace;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Service {
|
||||
Engineering,
|
||||
Marketing,
|
||||
Sales,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Employee {
|
||||
name: String,
|
||||
service: Service,
|
||||
}
|
||||
|
||||
// https://www.reddit.com/r/rust/comments/2vqama/parse_string_as_enum_value/cojzafn/
|
||||
impl FromStr for Service {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Service, ()> {
|
||||
match s {
|
||||
"engineering" => Ok(Service::Engineering),
|
||||
"marketing" => Ok(Service::Marketing),
|
||||
"sales" => Ok(Service::Sales),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_command(employees: &mut Vec<Employee>, cmd: &str) -> Vec<Employee> {
|
||||
let cmd = String::from(cmd);
|
||||
let mut iter = cmd.split_whitespace();
|
||||
|
||||
let employees = match iter.next() {
|
||||
Some(a) => match a.to_lowercase().as_str() {
|
||||
"add" => add_employee(employees, iter),
|
||||
"remove" => remove_employee(employees, iter),
|
||||
"list" => list_employees(employees.to_vec(), iter),
|
||||
_ => {
|
||||
println!("Valid actions are Add, Remove and List.");
|
||||
employees.to_vec()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
println!("You need to write an action");
|
||||
employees.to_vec()
|
||||
}
|
||||
};
|
||||
|
||||
employees
|
||||
|
||||
// Regex attempt
|
||||
// let re = Regex::new(r"(?P<action>\w+) (?P<name>\w+) to (?P<service>\w+)").unwrap();
|
||||
// let caps = match re.captures(cmd) {
|
||||
// Some(c) => c,
|
||||
// None => {
|
||||
// println!("Command not understood");
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
// println!(
|
||||
// "{:?}, {:?}, {:?}",
|
||||
// &caps["action"], &caps["name"], &caps["service"]
|
||||
// );
|
||||
}
|
||||
|
||||
fn add_employee(employees: &mut Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
|
||||
let mut new_employee = Employee {
|
||||
name: String::from(""),
|
||||
service: Service::Engineering,
|
||||
};
|
||||
new_employee.name = if let Some(name) = iter.next() {
|
||||
String::from(name)
|
||||
} else {
|
||||
println!("Enter the employee's name and service.");
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
if let None = iter.next() {
|
||||
println!(r#"You need the "to" preposition in order to form an intelligible sentence."#);
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
new_employee.service = if let Some(service) = iter.next() {
|
||||
match Service::from_str(&service.to_lowercase()) {
|
||||
Ok(srv) => srv,
|
||||
Err(_) => {
|
||||
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
|
||||
return employees.to_vec();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
println!("Adding {} to {:?}", new_employee.name, new_employee.service);
|
||||
employees.push(new_employee);
|
||||
employees.to_vec()
|
||||
}
|
||||
|
||||
fn remove_employee(employees: &mut Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
|
||||
let mut delete_employee = Employee {
|
||||
name: String::from(""),
|
||||
service: Service::Engineering,
|
||||
};
|
||||
delete_employee.name = if let Some(name) = iter.next() {
|
||||
String::from(name)
|
||||
} else {
|
||||
println!("Enter the employee's name and service.");
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
if let None = iter.next() {
|
||||
println!(r#"You need the "from" preposition in order to form an intelligible sentence."#);
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
delete_employee.service = if let Some(service) = iter.next() {
|
||||
match Service::from_str(&service.to_lowercase()) {
|
||||
Ok(srv) => srv,
|
||||
Err(_) => {
|
||||
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
|
||||
return employees.to_vec();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
|
||||
return employees.to_vec();
|
||||
};
|
||||
|
||||
let deleted = employees
|
||||
.iter_mut()
|
||||
.position(|n| n.name == delete_employee.name && n.service == delete_employee.service);
|
||||
match deleted {
|
||||
Some(x) => {
|
||||
let removed = employees.remove(x);
|
||||
println!("Fired {:?} !", removed);
|
||||
}
|
||||
None => println!("No employee fired."),
|
||||
};
|
||||
|
||||
employees.to_vec()
|
||||
}
|
||||
|
||||
fn list_employees(employees: Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
|
||||
let service = if let Some(service) = iter.next() {
|
||||
match Service::from_str(&service.to_lowercase()) {
|
||||
Ok(srv) => Some(srv),
|
||||
Err(_) => {
|
||||
if &service.to_lowercase() == "all" {
|
||||
None
|
||||
} else {
|
||||
println!(
|
||||
"Precise the employee's service: Engineering, Marketing, Sales or all."
|
||||
);
|
||||
// return employees;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Precise the employee's service: Engineering, Marketing, Sales or all.");
|
||||
// return Err("Precise the employee's service: Engineering, Marketing, or Sales.");
|
||||
None
|
||||
};
|
||||
|
||||
let mut list = employees.to_owned();
|
||||
if let Some(srv) = service {
|
||||
println!("Keeping only the employees from the selected service ({:?})", srv);
|
||||
list.retain(|empl| empl.service == srv);
|
||||
}
|
||||
|
||||
list
|
||||
}
|
37
src/main.rs
Normal file
37
src/main.rs
Normal file
@ -0,0 +1,37 @@
|
||||
mod math_vector;
|
||||
mod pig_latin;
|
||||
mod company;
|
||||
|
||||
fn main() {
|
||||
math_vector::showroom();
|
||||
pig_latin::showroom();
|
||||
|
||||
let mut employees: Vec<company::Employee> = vec![];
|
||||
|
||||
// Test fail cases
|
||||
company::parse_command(&mut employees, "");
|
||||
company::parse_command(&mut employees, "Toto");
|
||||
company::parse_command(&mut employees, "Add Toto");
|
||||
company::parse_command(&mut employees, "Add Toto Sales");
|
||||
company::parse_command(&mut employees, "Add Toto to Streamer");
|
||||
|
||||
|
||||
company::parse_command(&mut employees, "Add Sally toto Engineering");
|
||||
company::parse_command(&mut employees, "Add Amir to Sales");
|
||||
company::parse_command(&mut employees, "Add Sarah to Marketing");
|
||||
company::parse_command(&mut employees, "Add Amir to Engineering");
|
||||
company::parse_command(&mut employees, "Add Paul to Engineering");
|
||||
company::parse_command(&mut employees, "Add Paul to Engineering");
|
||||
println!("{:?}", employees);
|
||||
|
||||
company::parse_command(&mut employees, "Remove Amir from Sales");
|
||||
company::parse_command(&mut employees, "Remove Paul from Engineering");
|
||||
company::parse_command(&mut employees, "Remove Sarah from Sales");
|
||||
println!("{:?}", employees);
|
||||
|
||||
println!("{:?}", company::parse_command(&mut employees, "List Engineering employees"));
|
||||
println!("{:?}", company::parse_command(&mut employees, "List Marketing employees"));
|
||||
println!("{:?}", company::parse_command(&mut employees, "List Sales employees"));
|
||||
|
||||
println!("{:?}", company::parse_command(&mut employees, "List all employees"));
|
||||
}
|
108
src/math_vector.rs
Normal file
108
src/math_vector.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn showroom() {
|
||||
println!("Calculate the Mean, Median and Mode of some vectors");
|
||||
|
||||
let v = vec![1, 2, 8, 5, 1, 9, 5, 4, 6, 5, 5];
|
||||
println!("Vector: {:?}", v);
|
||||
match mean(&v) {
|
||||
Some(x) => println!("Mean: {:?}", x),
|
||||
None => println!("Not possible to process the Mean of this Vec<T>."),
|
||||
}
|
||||
match median(&v) {
|
||||
Some(x) => println!("Median: {:?}", x),
|
||||
None => println!("Not possible to process the Median of this Vec<T>."),
|
||||
}
|
||||
match mode(&v) {
|
||||
Some(x) => println!("Modes: {:?}", x),
|
||||
None => println!("Not possible to process the Modes of this Vec<T>."),
|
||||
}
|
||||
|
||||
let v = vec![1, 2, 8, 7, 8, 6, 7, 5, 1, 9, 5, 4, 6, 5, 5, 9, 9, 9, 9];
|
||||
println!("Vector: {:?}", v);
|
||||
match mean(&v) {
|
||||
Some(x) => println!("Mean: {:?}", x),
|
||||
None => println!("Not possible to process the Mean of this Vec<T>."),
|
||||
}
|
||||
match median(&v) {
|
||||
Some(x) => println!("Median: {:?}", x),
|
||||
None => println!("Not possible to process the Median of this Vec<T>."),
|
||||
}
|
||||
match mode(&v) {
|
||||
Some(x) => println!("Modes: {:?}", x),
|
||||
None => println!("Not possible to process the Modes of this Vec<T>."),
|
||||
}
|
||||
|
||||
let v = vec![];
|
||||
println!("Vector: {:?}", v);
|
||||
match mean(&v) {
|
||||
Some(x) => println!("Mean: {:?}", x),
|
||||
None => println!("Not possible to process the Mean of this Vec<T>."),
|
||||
}
|
||||
match median(&v) {
|
||||
Some(x) => println!("Median: {:?}", x),
|
||||
None => println!("Not possible to process the Median of this Vec<T>."),
|
||||
}
|
||||
match mode(&v) {
|
||||
Some(x) => println!("Modes: {:?}", x),
|
||||
None => println!("Not possible to process the Modes of this Vec<T>."),
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn mean(v: &Vec<i32>) -> Option<f64> {
|
||||
if v.len() == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut sum = 0;
|
||||
for elem in v {
|
||||
sum += elem;
|
||||
}
|
||||
|
||||
let sum = sum as f64;
|
||||
|
||||
Some((sum / v.len() as f64).into())
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/29578077
|
||||
pub fn median(v: &Vec<i32>) -> Option<f64> {
|
||||
if v.len() == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut v_sorted = vec![0; v.len()];
|
||||
v_sorted.copy_from_slice(&v);
|
||||
v_sorted.sort();
|
||||
|
||||
if v_sorted.len() % 2 == 0 {
|
||||
Some((v_sorted[(v_sorted.len() / 2) - 1] + v_sorted[(v_sorted.len() / 2) - 1]) as f64 / 2.)
|
||||
} else {
|
||||
Some(v_sorted[v_sorted.len() / 2].into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode(v: &Vec<i32>) -> Option<i32> {
|
||||
if v.len() == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut map = HashMap::new();
|
||||
for elem in v {
|
||||
let count = map.entry(elem).or_insert(0);
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
let mut mode_key = 0;
|
||||
let mut mode_val = 0;
|
||||
|
||||
for (key, val) in map.iter() {
|
||||
if val > &mode_val {
|
||||
mode_key = **key;
|
||||
mode_val = *val;
|
||||
}
|
||||
}
|
||||
|
||||
Some(mode_key)
|
||||
}
|
44
src/pig_latin.rs
Normal file
44
src/pig_latin.rs
Normal file
@ -0,0 +1,44 @@
|
||||
pub fn showroom() {
|
||||
println!("Change a world String to \"pig-latin\".");
|
||||
|
||||
let consonant = String::from("first");
|
||||
let vowel = String::from("apple");
|
||||
let symbol = String::from("!symbolic");
|
||||
let uppercase = String::from("UPPERCASE");
|
||||
let accent = String::from("école");
|
||||
let accent_multiples_char = String::from("église");
|
||||
|
||||
let consonant = to_pig_latin(consonant);
|
||||
println!("And the suffix to add is: {}", consonant);
|
||||
let vowel = to_pig_latin(vowel);
|
||||
println!("And the suffix to add is: {}", vowel);
|
||||
let symbol = to_pig_latin(symbol);
|
||||
println!("And the suffix to add is: {}", symbol);
|
||||
let uppercase = to_pig_latin(uppercase);
|
||||
println!("And the suffix to add is: {}", uppercase);
|
||||
let accent = to_pig_latin(accent);
|
||||
println!("And the suffix to add is: {}", accent);
|
||||
let accent_multiples_char = to_pig_latin(accent_multiples_char);
|
||||
println!("And the suffix to add is: {}", accent_multiples_char);
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn to_pig_latin(mut s: String) -> String {
|
||||
let vowels = vec!['a', 'e', 'i', 'o', 'u', 'y'];
|
||||
|
||||
// FIXME: is there something better than removing the 1st char like this ?
|
||||
// Since 2 of the 3 branches need to reform the word.
|
||||
let first_letter = s.remove(0);
|
||||
|
||||
if !first_letter.is_alphabetic() {
|
||||
// Not starting with a letter, ignore and reset the letter
|
||||
format!("{}{}", first_letter, s)
|
||||
} else if vowels.contains(&first_letter.to_ascii_lowercase()) {
|
||||
// Starting with a vowel, reform the word and place add "-hay"
|
||||
format!("{}{}-hay", first_letter, s)
|
||||
} else {
|
||||
// Starting with a consonant, place the consonant at the end of the word followed by "ay"
|
||||
format!("{}-{}ay", s, first_letter)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user