2

I am writing a program that takes an employee wages and hours worked, and outputs the yearly salary of each employee, which employee works the most, which employee makes the most, etc.

Here's what I have so far:

puts "Name of your employee"
employee_1 = gets.chomp!

puts "How much does #{employee_1} make per hour?"
employee_1_wage = gets.chomp!

puts "How many hours does #{employee_1} work per day?"
employee_1_hours = gets.chomp!

puts "Do you have another employee you would like to enter? If 
yes, press ENTER"

pause = STDIN.gets

puts "Name of your employee"
employee_2 = gets.chomp!

puts "How much does #{employee_2} make per hour?"
employee_2_wage = gets.chomp!

puts "How many hours does #{employee_2} work per day?"
employee_2_hours = gets.chomp!

puts "Do you have another employee you would like to enter? If 
yes, press ENTER"

pause = STDIN.gets

I want to make this a loop, but I don't even know where to start. And how would a loop automatically assign the first input as employee_1, second as employee_1_wage, and so on? Then for the second loop as employee_2, etc? Because later on I want to reference these in order to calculate the yearly salary for each employee, etc.

fool-dev
  • 7,671
  • 9
  • 40
  • 54
N.A
  • 135
  • 1
  • 6
  • 13
  • Welcome to SO. Please take the time to read https://stackoverflow.com/help/how-to-ask. It'll help you write solid questions that will (hopefully) yield valuable answers. Good luck! – orde Apr 12 '18 at 22:55

3 Answers3

3

Something like this:

# an array to hold as many employees as you wish
employees = []

loop do 
  # a hash to store data of one employee
  employee = {}

  puts "Name of your employee"
  employee[:name] = gets.chomp!

  puts "How much does #{employee[:name]} make per hour?"
  employee[:wage] = gets.chomp!

  puts "How many hours does #{employee[:name]} work per day?"
  employee[:hours] = gets.chomp!

  # adding the employee to the array
  employees << employee

  puts "Do you have another employee you would like to enter? If 
  yes, press ENTER. If no, press 'x'"

  command = STDIN.gets
  break if command.chomp! == "x"
end

Now you have an array of employees and can do whatever you want with them.

chumakoff
  • 6,807
  • 2
  • 23
  • 45
  • 1
    And after some successful loops, if you do `p employees` to print out the value in Ruby syntax, you will see a value like this: `[ {name: 'Barbara', wage: '$5', '4'}, {name: 'Alicia', wage: '$32', hours: '9'} ]`. You could get the name `'Alicia'` from that using `employees[1][:name]`. – Rory O'Kane Apr 12 '18 at 23:57
3

This is kind of a weird case. Because, first off, your instinct to loop is a good one. But you're in kind of an awkward situation because you haven't gotten to persisting data yet, which always makes these intro-level Ruby examples kind of hard to learn. At least in my opinion.

But, this one actually might be kind of fun so I'm going to try and provide you with a solution that I hope will make sense. Disclosure: this will be very long and you may not understand what's going on. I will do my best to try and break it down line-by-line, but you might still be frustrated by some aspects of my answer. Because in reality, there's maybe something simpler out there that you can understand. But if you follow what I've written, play with it, and really try to understand it I think you will be in a really good spot to understand how Ruby conventions work moving forward. Be patient and be open.

Copy what I've added at the bottom into your text editor and save it. Run the file by running: ruby whatever_you_named_the_file.rb

Run through it a few times, enter in one employee then enter in the data for multiple employees. See how you can make it break. then look at the code change stuff around to see how it changes the output.

To address your point of "how would a loop automatically assign the input as employee_1?" The answer is, it won't. A loop has to be agnostic to the variables it assigns. Meaning all of the assignments will simply be to employee, and each "instance" of a loop will only deal with one employee.

Anyway, here's the full code. Beneath this I'll try to break down each section and add comments throughout:

Here's the full code which you can put in an editor to mess around with:

$employees_array = []

def get_employee_data
  puts "Name of your employee"
  employee = gets.chomp!

  puts "How much does #{employee} make per hour?"
  employee_wage = gets.chomp!

  puts "How many hours does #{employee} work per day?"
  employee_hours = gets.chomp!

  add_employee(employee, employee_wage, employee_hours)

  puts "Do you have another employee you would like to enter?"
  response = gets.chomp!

  if response.downcase == "no"
    display_employee_salaries
  else
    get_employee_data
  end
end

def add_employee(employee, employee_wage, employee_hours)
  employee_hash = {
    name: employee,
    wage: employee_wage.to_i,
    hours: employee_hours.to_i,
  }
  $employees_array << employee_hash
end

def display_employee_salaries
  $employees_array.each do |employee|
    puts "Employee #{employee[:name]} makes $#{employee[:wage]} per hour and worked #{employee[:hours]}. That means their annual salary is: $#{ employee_salary(employee) } "
  end
end

def employee_salary(employee)
  employee[:wage] * employee[:hours] * 261 # 261 is the number of working days per year
end

get_employee_data

Now, to try and break that down:

$employees_array = [] # This is a global variable that will simulate a database for us so we can keep track of our employees.

def get_employee_data #the first half of this is largely similar to what you have. It will prompt the user for information.
  puts "Name of your employee"
  employee = gets.chomp!

  puts "How much does #{employee} make per hour?"
  employee_wage = gets.chomp!

  puts "How many hours does #{employee} work per day?"
  employee_hours = gets.chomp!

  # Now we should hand this data off to another method, so that we can continue our loop and get more employees.
  add_employee(employee, employee_wage, employee_hours) # this is a call to a method we define below. It passes all of the information gathered in the first loop so we can add another employee.

  puts "Do you have another employee you would like to enter?"
  response = gets.chomp!

  # Now we decide if we want to loop again or return the output based on what the user says
  if response.downcase == "no" #if the user says no, that's when we know to break the loop and calculate our salary data.
    display_employee_salaries # this is another method that we've defined below, it is the final step to the loop.
  else
    get_employee_data # if a user says *anything* other than "no", we call this method, which will restart this loop. This might be confusing, that's because this is known as recursion--which can be a difficult concept to grasp. Be patient.
  end
end

This is the first new method we need to define. What this does is creates a Hash of all of the data we gathered for one employee, it contains a "name", "wage", and "hours" based on what the user gave us in the previous method (above).

def add_employee(employee, employee_wage, employee_hours)
  employee_hash = {
    name: employee,
    wage: employee_wage.to_i,
    hours: employee_hours.to_i,
  }
end

Remember the global variable at the top of the file? Now we just add this hash to the global Array.

$employees_array << employee_hash

The first time this runs the array will be empty, i.e.: []

The second time this runs the array will have an employee_hash inside: [employee_hash]

If another employee is entered, this line is executed again so the array will have [employee_hash, employee_hash]

This can happen for as many employees as you enter. So if you enter an employee 5 times the array will look like: [employee_hash, employee_hash, employee_hash, employee_hash, employee_hash]

You may be wondering "aren't those all the same? how does it know the difference?" The variable names are the same, the contents of the Hash are different. It doesn't know the difference. But it doesn't have to.


The second method we define is below. This gets called if the user answers "no" when asked "Do you have another employee you would like to enter?"

The responsibility of this method is to print out the employee salaries once all the employees have been entered.

To do this, we iterate (loop) through the global array of employees, and for each entry in that array, we do a puts command to print out our data. This is happening when we call ".each". It's saying, "For each item in this array, call it an "employee", and perform these actions". Once the array runs out of items, the loop ends and the program exits.

def display_employee_salaries
  $employees_array.each do |employee|
    puts "Employee #{employee[:name]} makes $#{employee[:wage]} per hour and worked #{employee[:hours]}. That means their annual salary is: $#{ employee_salary(employee) } "
  end
end

Couple things to note: If you're unfamiliar with hash lookups- this is an example: employee[:name] what this is doing, is it's looking for a "name" key in a hash called "employee". If there is a "name" key, then it will return to us the value. So if "employee" is a hash that looks like this:

{ name: "ActiveModel_Dirty", wage: "40", hours: "8" }

then employee[:name] will return "ActiveModel_Dirty", employee[:wage] will return "40", and so forth.

You may be wondering: "Hey, we called that hash employee_hash earlier, why are we suddenly referring to it as "employee"? Good question. after the .each do below you'll see |employee|. "employee" here can be whatever you want. it is how we refer to the current item we're iterating over only while inside this loop, it has no bearing on anything other than a reference point.

Lastly, you will notice a call to employee_salary towards the end of the puts command. This is calling another method that we define below.


The final method is used to do math——calculate an annual salary based on the information that the user gave us about an employee. It recieves an instance of an employee as an argument (that's what the (employee)) is.

It will calculate the wage, multiplied by the employee hours, multiplied by the working days in a year. Once it performs this calculation Ruby by default will return whatever it came up with. Which we then print to the console in the 'puts' statement above.

This method returns a number, that number is the value for the annual salary

def employee_salary(employee)
  employee[:wage] * employee[:hours] * 261 # 261 is the number of working days per year
end

I really hope that helps you out and I hope it's not overwhelming. No one says you need to know how all of it works right now. Process as much as you can and then come back to it if you need to.

  • Calling `get_employee_data` recursively is an interesting technique that might be appropriate in some programming languages, but I wouldn’t try to teach it to a beginner as a way of looping in Ruby. Ruby [doesn’t necessarily](https://stackoverflow.com/q/824562/578288) perform tail call optimization, meaning in some implementations of Ruby, there would be a stack overflow if you looped too many times. It’s unlikely to happen in the case of entering employees, but you need that disclaimer in case they try using the same technique in another program. – Rory O'Kane Apr 13 '18 at 00:17
  • It’s a good idea to mix explanations with the code, but it doesn’t render well on Stack Overflow since text in code blocks doesn’t wrap. You could manually wrap the comments into multiple lines, but I think this style of explanation would look even better if you wrote normal paragraphs interspersed with the code blocks. It would look like [Literate CoffeeScript](http://coffeescript.org/#literate). – Rory O'Kane Apr 13 '18 at 00:35
  • Recursion in Ruby is very rarely the way to go. But encapsulating logic in methods is a core principle of OOP. This was an end-to-justify-the-means, intent being to exemplify how an actual ruby program may look vs a one-off solution to the problem at hand. Tbh, it's not what I'd dub the type of recursive call that beginners find confusing. Usually that disconnect happens when you have data that mutates itself, automatically grows in size, and has branching conditional logic within. It's basically just the inverse of a `break if` in a `loop do`,which I don't think is inherently harder to follow – ActiveModel_Dirty Apr 13 '18 at 01:11
  • I agree with your second comment, I did suggest copying the text to an editor before parsing, which may help a bit. As a beginner, I always found it difficult to read an explanation and interpret the code that follows, because I would fear I was interpreting incorrectly. I wanted to give the user something they could reference here. But you're correct in that it looks awful on stack overflow. In general I provide my answers as you suggested, but the OP here seems to be in an earlier stage of learning so I wanted to do something different from the other responses in case they weren't clicking. – ActiveModel_Dirty Apr 13 '18 at 01:18
  • @RoryO'Kane appreciate the feedback in any case. I did wind up updating the formatting of my answer to closer fit a stack overflow-y style. – ActiveModel_Dirty Apr 13 '18 at 01:44
0

Here's a simple starter script, tested (rudimentary) and works, you can flesh this out with your full list of prompts and responses, and clearly your own global (kernel) method "process" would be more elaborate:

#!/usr/bin/env ruby

# filename: prompted_loop_example.rb

def process( emp, pay )
  pp emp, pay
end # process

loop do
  puts "Employee name (<Enter> or Ctrl/D to quit): "
  emp1 = STDIN.gets.chomp!
  break if emp1 == "" || emp1.nil?
  puts "Pay scale: "
  pay1 = STDIN.gets.chomp!
  process( emp1, pay1 )
  end

exit 0