"number" not a declared variable but still works? - ruby

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.

Related

Takes a list of strings and returns each line prepended by the correct number: Ruby

Working on a task to create a function which takes a list of strings and returns each line prepended by the correct number.
## Prepend each number to a list of strings sequentially.
def number lines
lines.map {|numbered_lines| numbered_lines.prepend("1: ")}
end
## Problem: How to create a list of numbers that prepend to list of strings???
for example
number ["a", "b", "c"] # => ["1: a", "2: b", "3: c"]
My code currently outputs the following:
should add line numbers (1-based index)
Expected: ["1: a", "2: b", "3: c"], instead got: ["1: a", "1: b", "1: c"]
Any useful resources on how I can work around this type of task in future will be much appreciated. Thanks.
You can make use of with_index to start from index 1 instead of 0.
Iterates the given block for each element with an index, which starts from offset
Try the below:
def number_lines
["a", "b", "c"].map.with_index(1){ |element, index| "#{index}: #{element}" }
end
About with_index
By default with_index iterates the block for every element starting from 0th index
["a", "b", "c"].each.with_index { |element, index| puts "index: #{index} element: #{element}" }
The above snippet will output the below:
index: 0 element: a
index: 1 element: b
index: 2 element: c
with_index also accepts an optional argument to identify which index to start from, You can use with_index(3) to use the starting index as 3. Please find the below example for better understanding:
["a", "b", "c"].each.with_index(3) { |element, index| puts "index: #{index} element: #{element}" }
# Output
index: 3 element: a
index: 4 element: b
index: 5 element: c
Similarly to other answers, you could do
def number(lines)
(0...lines.size).each{|i| lines[i].prepend("#{i+1}: ")}
end
Note that this solution alters the input array itself and returns an enumerable range. If you don't want to modify the existing array, then I'd suggest:
def number(lines)
(0...lines.size).map{|i| "#{i+1}: #{lines[i]}" }
end
I often prefer iterating over the index range itself rather than the list if I need the index. It's a bit shorter and only has a single extra variable, but it's really just personal preference.
will it work for you?
def number lines
lines.each_with_index.map { |line,index| line.prepend("#{index} : ") }
end

Is there a more elegant way of writing a while loop in Ruby where the array size is not known?

Using the following example:
array = [1,20]
new_array = []
i = array[0]
while i < array[1]
new_array.push(i)
i+= 2
end
#new_array = [1,3,5,7,9,11,13,15,17,19]
Is there a more elegant way to write this loop without have to write an empty array (new_array) and an external variable loop counter (i)? I was thinking something along the lines of new_array.map{|x| } but instead of iterating through each element, it continually adds a number until it hits a certain limit.
Assuming your goal is to create an array of odd numbers up to a limit, you can use a range with a step.
limit = 20
array = (1..limit).step(2).to_a
EDIT
If you want to be able to descend as well as ascend you can use step.
#Ascending
start = 1
limit = 20
array = start.step(limit, 2).to_a
#Descending
start = 20
limit = 1
array = start.step(limit, -2).to_a
For the sake of having an alternative, you could also select (Enumerable#select) odds numbers (Integer#odds?) out of your Range:
(1..20).select(&:odd?)
#=> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
My answer is addressed to the question stated in the title, not to the particular example presenteed.
Suppose the user is asked to enter a sequence of strings, with an empty string signifying that the user is finished. These strings are to be saved in an array which is to be returned.
This is a typical way of writing the code using a while loop:
def gettem
arr = []
until (s = gets.chomp).empty? do
arr << s
end
arr
end
One could instead use Kernel#loop and the keyword break, which some (including me) prefer to while and until loops.
def gettem
arr = []
loop do
s = gets.chomp
break if s.empty?
arr << s
end
arr
end
A third way, suggested by #Aleksei Matiushkin in his answer here (which I had not seen before) is the following:
def gettem
loop.with_object([]) do |_,arr|
s = gets.chomp
break arr if s.empty?
arr << s
end
end
This uses the form of loop that returns an enumerator (see the doc). If I run this and enter "dog", "cat" and "\n", the return value is ["dog", "cat"], as desired.
This approach has three of advantages over the other approaches:
the variable arr is confined to the block, away from prying eyes;
fewer lines of code are needed; and
the return value can be chained, as illustrated below.
def gettem
loop.with_object([]) do |_,arr|
s = gets.chomp
break arr if s.empty?
arr << s
end.then { |arr| [arr.size, arr] }
end
When executing this method and entering "dog", "cat" and "\n", the array [2, ["dog", "cat"]] is returned.
I've used an underscore for the first block variable (which always has a value of nil) to signify that it is not used in the block calculation.

How to print all key value pairs in Ruby hash?

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)

Why does my function not print the sorted array?

# 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

Ruby Range printing out extra

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"

Resources