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.
I've been seriously enjoying learning some Rust and reading the Rust book recently, and want to get a little feedback on my answer to this challenge from chapter 8.
I've deliberately kept things fairly short and simple - I've answered the question very literally and all of the criteria are met as far as I can tell from my testing.
I hard-coded the departments into the hashmap and I didn't try very hard to handle edge cases or improper input. Users also have to type in very clunky commands with exact wording.
The interface is not super user-friendly as after every command the loop restarts and the welcome message is shown each time.
Feel free to provide feedback on any of the above but know that I am at least aware of these issues.
I'm mostly concerned about idiomatic Rust and code structure.
Is there a better way for me to handle the various conditions than with if
/ else if
/ else
?
I didn't want to nest too many loops in the main function for readability and my own sanity's sake. If I wanted to make the UX a little more seamless would it make sense to create a function for each command that might be called repeatedly i.e Add employees, View department, with an internal loop?
Anything else that stands out that I'm doing completely wrong?
Anything that stands out that I'm doing right?
use std::collections::HashMap;
use std::io;
fn main() {
let mut company: HashMap<String, Vec<String>> = HashMap::new();
company.insert("engineering".to_string(), Vec::new());
company.insert("operations".to_string(), Vec::new());
company.insert("sales".to_string(), Vec::new());
loop {
println!("Welcome! What would you like to do?\n
To add employee to a department, type 'Add <employee name> to <department name> (engineering, operations, or sales),\n
To view employees in a department, type 'View department <department name>',\n
To view all employees by department, type 'View all',
To exit, type 'exit'.
");
let mut entry = String::new();
io::stdin()
.read_line(&mut entry)
.expect("Failed to read line");
let entry: &str = entry.trim();
let mut split = entry.split_whitespace();
if entry.contains("Add") {
let emp = split.nth(1).unwrap().to_string();
let dep = split.nth(1).unwrap().to_lowercase();
println!("{emp} added to {dep}");
let vec = company.get_mut(&dep).unwrap();
vec.push(emp);
vec.sort_by_key(|name| name.to_lowercase());
println!("{:?}", company)
} else if entry.contains("View department") {
let dep = split.nth(2).unwrap().to_lowercase();
let view = &company[&dep];
println!("Employees in {dep}:\n");
for v in view {
println!("{v}\n");
}
} else if entry.contains("View all") {
for k in company.keys() {
let view = &company[k];
println!("Department: {k}");
for v in view {
println!("{v}\n");
}
}
} else if entry == "exit" {
break;
} else {
println!("Please enter a valid option");
}
}
}
entry.contains("Add")
expression is a bit too permissive -- it matchesView department Addiction Treatment Services
. Better to ask ifentry
starts with"Add "
. Also, indep = split.nth(1)...
, it would seem like we wantnth(2)
, but I'm sure you've tested it so I guess not. Oh, and listen to everything @corvus_192 says in that excellent review. \$\endgroup\$dep = split.nth(1)
, it has to be that way instead of a higher nth() because afteremp = split.nth(1)
the first two indices ofsplit
get consumed! It's an interesting behavior that definitely tripped me up for a bit while I was working it out. \$\endgroup\$