# Methods for calculating
# print out the input that the user entered
def PrintScores(*numbers)
numbers.each {|x| print x.join(" ")}
puts
end
#print out the scores in ascending order
def ListScores(*numbers)
numbers.sort!
print numbers
end
# Main function
out_file = File.new("out.txt", "w")
puts "Enter the scores you wish to have our stats program look into? "
user_input = gets.chomp
input_array = user_input.split(" ")
input_array.map! do |x|
x.to_i
end
PrintScores(input_array)
ListScores(input_array)
The ListScores function still prints the array in the order in which I entered it, and I can not figure out why.
The ListScores function still prints the array in the order in which I entered it and i can not figure out why?
In your current code, input_array is an instance of Array class which is passed as an argument to ListScores method. ListScores is expecting splat arguments, so numbers become an Array containing a single Array element(i.e., input_array contents). This is the reason you see the array in the same order when you try to sort it.
For example:
> user_input = gets.chomp
3 2 8 5 1
=> "3 2 8 5 1"
> input_array = user_input.split(" ")
=> ["3", "2", "8", "5", "1"]
> input_array.map! do |x|
> x.to_i
> end
=> [3, 2, 8, 5, 1]
> ListScores(input_array)
[[3, 2, 8, 5, 1]] => nil ## Notice Array with single Array element [[]]
splat operator(*) is used in methods when you have a need for variable parameter list.
In your case, you don't need splat operator in PrintScores and ListScores method.
def PrintScores(numbers) ## <-- Removed splat operator
numbers.each {|x| print x.join(" ")}
puts
end
#print out the scores in ascending order
def ListScores(numbers) ## <-- Removed splat operator
numbers.sort!
print numbers
end
Sample output:
> ListScores(input_array)
[1, 2, 3, 5, 8] => nil
NOTE: Its advisable to use snake_case for method name like list_scores instead of ListScores
Related
My apologies for the potentially stupid question, I'm an absolute beginner to Ruby and code in general.
I have set up a hash with some predetermined values. I want to ask the user for input, if that input matches an existing key, I want the corresponding value to be updated (+ 1, in this case). Then I want to print all the current up-to-date values.
hash = {"apple": 6, "banana": 2, "carrot": 3}
order = gets.chomp.downcase
hash.each do |key, value|
if key.to_s == order
value += 1
puts "Your order includes: #{value} #{key}."
end
end
My problem is that I only know how to print a single key value pair.
E.g. if the user inputs "apple", I'd like the output to say "Your order includes: 7 apple, 2 banana, 3 carrot."
hash = {apple: 6, banana: 2, carrot: 3}
order = gets.chomp.downcase.to_sym
hash[order] = hash.fetch(order, 0) + 1
puts "Your order includes: " + hash.map { |k, v| "#{v} #{k}" }.join(", ")
Some notes:
your hash initialization hash = {"apple": 6, "banana": 2, "carrot": 3}. the keys of your hash seem strings, but if you use that syntax with the colon, they become symbols. So, you have two choice. this syntax:
hash = {"apple" => 6, "banana" => 2, "carrot" => 3}
or you can use symbols as I did and convert the user input in a symbol
what's really cool about hash is that you don't need to iterate through the elements to find what you're looking for. There's a mapping between keys and values, so it's easy find and update a value
in the third row, I'm dealing with the fact that the key could not be in the hash, I used fetch to have 0 in that case. then, I increment and I assign back to that key
The question does not specify if you want to mutate the initial hash, so I suppose you do. Then the following will do.
hash = Hash.new(0).merge(apple: 6, banana: 2, carrot: 3)
hash[gets.chomp.downcase.to_sym] += 1
puts "Your order includes: " <<
hash.map { |k, v| [v, k].join(' ') }.join(', ')
or:
puts hash.reduce("Your order includes: ") { |acc, (k, v)|
acc << "#{v} #{k}, "
}[0..-3]
Consider to initialize the hash providing a default value (Hash#default)
basket = {'apple' => 6, 'banana' => 2, 'carrot' => 3}
basket.default = 0 # <-- set default 0 for start counting new keys
Define a method to present the data:
def show(basket)
puts "Your order includes:"
basket.each{ |key, value| puts "#{value}: #{key}" }
end
Capture user input in a loop (explanation in comments):
loop do
puts "Place your order:"
order = gets.downcase.chomp # <-- format the input
break if order == '0' # <-- breaks the input loop if this contition is matched
next unless basket.has_key? order # <-- skip to next loop no matches with hash keys or remove this line if you want to count also non initialised keys
basket[order] += 1 # <-- increment by one the key
show(basket) # <-- call the metod to show the basket
end
show(basket)
I have:
class Thing
def initialize
#array = [[0, 0, 0], [1, 1, 1]]
end
end
thing = Thing.new
The normal way to access an element in #array is to use [] as in:
#array[0][1] # => 0
I am trying to overwrite [] so as to get results like this:
position_array = [0, 1]
#array[position_array] # => 0
This is my attempt:
class Thing
def [](position_array)
index_row, index_col = position_array
#array[index_row][index_col]
end
def get_value(position_array)
#array[position_array] # doesn't work
# self[position_array] # does work
end
end
thing.get_value([0, 1])
# >> 'get_value': no implicit conversion of Array into Integer (TypeError)
Why do I need to index the Thing object in order to index #array?
Just think of message and receiver.
#array[position_array] sends the message [] to the receiver #array. #array is an instance of Array, so the method Array#[] gets invoked.
self[position_array] sends the message [] to the receiver self. Within instance methods, self refers to that instance. And because self is an instance of Thing, the method Thing#[] gets invoked.
Since Thing is a subclass of Object and not a subclass of Array (nothing wrong here, you shouldn't subclass Array anyway), your implementation of [] does not override Array#[]. Both methods are totally independent of each other, just like String#[] or Hash#[].
This is how I would approach it:
class Thing
def initialize
#array = [[1, 2, 3], [4, 5, 6]]
end
def [](i, j)
#array[i][j]
end
end
thing = Thing.new
thing[0, 1] #=> 2
thing[1, 1] #=> 5
You could use a prepended method to non-invasively override the [] method in Array by duck-typing the parameter passed to the [] method, and then calling the original if its not what you expect. Then you don't need a Thing object at all.
module MyArrayExtension
def [] (*param)
if param.size == 2
row, col = param
raise ArgumentError, 'Row must be an integer' if row.class != Integer
raise ArgumentError, 'Column must be an integer' if col.class != Integer
raise ArgumentError, "Element at row #{row} is not an array" if self[row].class != Array
self[row][col]
else
super
end
end
end
class Array
prepend MyArrayExtension
end
thing = [[1,2,3],[4,5,6]]
puts "The 2D array is: #{thing}"
puts "Extension used on the thing to get at element 1 of first array:"
puts thing[0,1]
puts '-' * 20
normal = [1,2,:blah,4,5]
puts "Normal array is #{normal}"
puts "Original [] method used to get the 3rd element:"
puts normal[2]
puts '-' * 20
puts "Using the extension on the non-2D array:"
puts normal[0,1]
The output of this program is:
The 2D array is: [[1, 2, 3], [4, 5, 6]]
Extension used on the thing to get at element 1 of first array:
2
--------------------
Normal array is [1, 2, :blah, 4, 5]
Original [] method used to get the 3rd element:
blah
--------------------
Using the extension on the non-2D array:
./test.rb:9:in `[]': Element at row 0 is not an array (ArgumentError)
from ./test.rb:35:in `<main>'
I am trying to do something similar to this:
def foo(mode= :serial)
if (mode == :serial) then
self.send(:bar, "one_type")
else
self.send(:bar,"second_type",:T5)
end
end
I can obviously type this out like this.
But I've recently tried expanding it to include a second function like this:
def foo(mode= :serial)
if (mode == :serial) then
self.send(:bar, "one_type")
self.send(:foobar, "one_type",20)
else
self.send(:bar,"second_type",:T5)
self.send(:foobar, "one_type",20,:T5)
end
end
I can still continue as it is, but I thought to myself, there's a pattern here, I should abstract the arguments away into another layer and make this simpler.
So what I wanted to do was something like this:
arg_arr = [:bar, "one_type"]
arg_arr_2 = [:foobar, "one_type", 20]
if (mode == :serial) then
self.send(arg_arr)
self.send(arg_arr_2)
else
arg_arr << :T5
arg_arr2 << :T5
self.send(arg_arr)
self.send(arg_arr2 )
end
I tried some other ideas involving .each, .inspect, but nothing that would work (the usual error was can't convert array into string, which I'm guessing refers to the fact that it treats the array as the entire function name). I can do it if I explicitly say "use array elements[0] , [1] etc, but that just seems wasteful.
Is there a way to achieve this without writing code that is hardcoded to the number of arguments?
Try this
def foo(a, b)
puts a
puts b
end
array = ['bar', 'qux']
send(:foo, *array) # using send
foo(*array) # using the method name
Both print
bar
qux
The splat operator * packs or unpacks an array.
Some years ago I did what you are trying now. With an asterisk in front of a method parameter you can receive as many parameters as you want in a function. So You don't need to know the number of the given parameters. It's called a splat.
Send your values as an array with an asterisk in front too and it will work.
I tested the folling with an irb console:
def test(*args)
puts args.inspect
end
my_args = [1, 2, 3]
self.send(:test, *my_args)
# [1, 2, 3]
# => nil
Or send as many single parameters as you want:
self.send(:test, 'a', 'b', 'c', 'd')
# ["a", "b", "c", "d"]
# => nil
If you have a fixed number of parameters this will work:
def test(arg1, arg2, arg3)
puts arg1.inspect
puts arg2.inspect
puts arg3.inspect
end
my_args = [1, 2, 3]
self.send(:test, *my_args)
# 1
# 2
# 3
# => nil
First, you shouldn't use send. You could use public_send, Method#call or just bar(...) if you know the method name.
Homogenous parameters
If the parameters are homogenous (e.g. are instances of the same Class), you can just put them in an Array, and use this Array as parameter :
def analyze_array(array)
puts "Elements : #{array}"
puts "Length : #{array.size}"
puts "Sum : #{array.inject(:+)}"
puts
end
analyze_array([1,2,3])
analyze_array([1,2,3,4,5])
It outputs :
Elements : [1, 2, 3]
Length : 3
Sum : 6
Elements : [1, 2, 3, 4, 5]
Length : 5
Sum : 15
Example
Refactoring your code a bit, it could become :
arg_arr = [:bar, 1]
arg_arr_2 = [:foobar, 1, 2]
def bar(array)
puts " bar with one parameter : #{array}"
end
def foobar(array)
puts " foobar with one parameter : #{array}"
end
[:serial, :parallel].each do |mode|
puts "Mode : #{mode}"
[arg_arr, arg_arr_2].each do |method_and_args|
method_name, *args = method_and_args
args << 3 if mode != :serial
method(method_name).call(args)
end
end
It outputs :
Mode : serial
bar with one parameter : [1]
foobar with one parameter : [1, 2]
Mode : parallel
bar with one parameter : [1, 3]
foobar with one parameter : [1, 2, 3]
Heterogenous parameters
For an unknown number of parameters that might belong to different classes, you can use the splat operator (documentation) :
def analyze_parameters(*params)
puts "Parameters : #{params}"
puts "Number : #{params.size}"
puts "Classes : #{params.map(&:class)}"
end
analyze_parameters('Test')
analyze_parameters(1, 'a', :b, [:c, :d])
It outputs :
Parameters : ["Test"]
Number : 1
Classes : [String]
Parameters : [1, "a", :b, [:c, :d]]
Number : 4
Classes : [Fixnum, String, Symbol, Array]
Your example becomes :
arg_arr = [:bar, 1 ]
arg_arr_2 = [:foobar, 1, 'a']
def bar(*params)
puts " bar with multiple parameters : #{params}"
end
def foobar(*params)
puts " foobar with multiple parameters : #{params}"
end
[:serial, :parallel].each do |mode|
puts "Mode : #{mode}"
[arg_arr, arg_arr_2].each do |method_and_args|
method_name, *args = method_and_args
args << :b if mode != :serial
method(method_name).call(*args)
end
end
It outputs :
Mode : serial
bar with multiple parameters : [1]
foobar with multiple parameters : [1, "a"]
Mode : parallel
bar with multiple parameters : [1, :b]
foobar with multiple parameters : [1, "a", :b]
I'm learning ruby, I wrote out this code as part of a course, however on line 9 the variable number is introduced but isn't declared, the console doesn't throw up an error, why is this? is it specifically part of the for loop?
#set an array counting up from 1 - 5
the_count = [1, 2, 3, 4, 5]
#array of fruits
fruits = ['apples', 'oranges', 'pears', 'apricots']
#mixed array of numbers and currency
change = [1, 'pennies', 2, 'dimes', 3, 'quarters']
# for each number in the_count array put...
for number in the_count
puts "this is count #{number}"
end
#for each element in the fruit array put...
fruits.each do |fruit|
puts "a fruit of type: #{fruit}"
end
#iterate through each element in change and on each of them preceed its value with "i got"
change.each {|i| puts "I got #{i}"}
#create an empty array
elements = []
#interate through numbers 0 - 5
(0..5).each do |i|
puts "adding to #{i} to the list."
#push each number to empty array
elements.push(i)
end
#iterate through each element in elements and preceed it with "Element was:"
elements.each {|i| puts "Element was: #{i}"}
in Ruby, variable are not declared. The interpreter decides a token is a variable if it's assigned a value or, like in this case, be used with for in syntax.
I'm new to coding so please free to point out any errors in the way I refer to code.
rows = 5
(1..rows).each do |n|
print n, ' '
end
This prints out what I expect it to: 1 2 3 4 5.
But, when I put it into a method:
def test(rows)
(1..rows).each do |n|
print n, ' '
end
end
puts test(5)
I get 1 2 3 4 5 1..5.
Why does the 1..5 show up? And how do I get rid of it?
I need it in the method because I plan to add more code to it.
each on a Range returns the range after the looping is done, and you're probably printing the return value of test too.
Just run test(5) instead of puts test(5) or something.
Ruby always returns the last line of any function.
You are executing puts test(5), and test(5) prints the data you expect, and the extra puts prints out the data returned by test(5) method.
Hope that answers your question.
The final 1..5 is the return value from the script. You get that when you run the code in IRB. When you run that as a standalone Ruby script, it will not show up, so you do not need to worry about it.
A Ruby function will return the last statement, in your case 1..5. To illustrate I'll give it a different return value:
def test(rows)
(1..rows).each {|n| puts "#{ n } "}
return 'mashbash'
end
# Just the function invokation, only the function will print something
test(5) # => "1 2 3 4 5 "
# Same as above, plus printing the return value of test(5)
puts test(5) # => "1 2 3 4 5 mashbash"
You could write your example a little differently to achieve what you like:
def second_test(rows)
# Cast range to an array
array = (1..rows).to_a # [1, 2, 3, 4, 5]
array.join(', ') # "1, 2, 3, 4, 5", and it is the last statement => return value
end
# Print the return value ("1, 2, 3, 4, 5") from the second_test function
p second_test(5)
# => "1, 2, 3, 4, 5"