From 9059b76493bc24546a2235647c85824aeba458d1 Mon Sep 17 00:00:00 2001 From: Zykino Date: Sat, 24 Nov 2018 21:12:16 +0100 Subject: [PATCH] Initial commit with my view on how to answer the exercices --- .gitignore | 2 + Cargo.lock | 4 + Cargo.toml | 7 ++ README.md | 10 +++ src/company.rs | 179 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 37 ++++++++++ src/math_vector.rs | 108 +++++++++++++++++++++++++++ src/pig_latin.rs | 44 +++++++++++ 8 files changed, 391 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/company.rs create mode 100644 src/main.rs create mode 100644 src/math_vector.rs create mode 100644 src/pig_latin.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8588a0d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "chapter8_exercices" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..52b5d97 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "chapter8_exercices" +version = "0.1.0" +authors = ["Zykino "] + +[dependencies] +# regex = "1" diff --git a/README.md b/README.md new file mode 100644 index 0000000..d76370c --- /dev/null +++ b/README.md @@ -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. diff --git a/src/company.rs b/src/company.rs new file mode 100644 index 0000000..8c601e7 --- /dev/null +++ b/src/company.rs @@ -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 { + match s { + "engineering" => Ok(Service::Engineering), + "marketing" => Ok(Service::Marketing), + "sales" => Ok(Service::Sales), + _ => Err(()), + } + } +} + +pub fn parse_command(employees: &mut Vec, cmd: &str) -> Vec { + 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\w+) (?P\w+) to (?P\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, mut iter: SplitWhitespace) -> Vec { + 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, mut iter: SplitWhitespace) -> Vec { + 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, mut iter: SplitWhitespace) -> Vec { + 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 +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dc293f2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,37 @@ +mod math_vector; +mod pig_latin; +mod company; + +fn main() { + math_vector::showroom(); + pig_latin::showroom(); + + let mut employees: Vec = 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")); +} diff --git a/src/math_vector.rs b/src/math_vector.rs new file mode 100644 index 0000000..f594901 --- /dev/null +++ b/src/math_vector.rs @@ -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."), + } + match median(&v) { + Some(x) => println!("Median: {:?}", x), + None => println!("Not possible to process the Median of this Vec."), + } + match mode(&v) { + Some(x) => println!("Modes: {:?}", x), + None => println!("Not possible to process the Modes of this Vec."), + } + + 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."), + } + match median(&v) { + Some(x) => println!("Median: {:?}", x), + None => println!("Not possible to process the Median of this Vec."), + } + match mode(&v) { + Some(x) => println!("Modes: {:?}", x), + None => println!("Not possible to process the Modes of this Vec."), + } + + 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."), + } + match median(&v) { + Some(x) => println!("Median: {:?}", x), + None => println!("Not possible to process the Median of this Vec."), + } + match mode(&v) { + Some(x) => println!("Modes: {:?}", x), + None => println!("Not possible to process the Modes of this Vec."), + } + + println!(); +} + +pub fn mean(v: &Vec) -> Option { + 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) -> Option { + 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) -> Option { + 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) +} diff --git a/src/pig_latin.rs b/src/pig_latin.rs new file mode 100644 index 0000000..d194ca0 --- /dev/null +++ b/src/pig_latin.rs @@ -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) + } +}