Can I build an array using a loop in Ruby? - ruby-on-rails

I just started learning Ruby/Rails, and am trying to write a program that builds an array, then formats the new array.
It works up to the second while, and, if I have an array already built, the second part works as well. Is there something I am leaving out?
chap = []
page = []
lineWidth = 80
x = 0
n = chap.length.to_i
puts 'chapter?'
chapter = gets.chomp
while chapter != ''
chap.push chapter
puts 'page?'
pg = gets.chomp
page.push pg
puts 'chapter?'
chapter = gets.chomp
end
puts ('Table of Contents').center lineWidth
puts ''
while x < n
puts ('Chapter ' + (x+1).to_s + ' ' + chap[x]).ljust(lineWidth/2) +(' page ' + page[x]).rjust(lineWidth/2)
x = x + 1
end
Thanks for your help!

Simple pilot error: You called
n = chap.length.to_i
too early. You have to get the length of the chap list AFTER you put things in it. Move that line here:
...
puts 'chapter?'
chapter = gets.chomp
end
n = chap.length.to_i
puts ('Table of Contents').center lineWidth
and it works fine.

Related

How do I make it look like the picture in Lua

This is my code
local level = 5
for i = 1, level do
local text = ""
for j = 1, i do
text = text..""
end
for j = 1, level-i, 1 do
text = text.." "
end
for j = 1+level, level+(level-i) do
text = text.." "
end
for j = 1, level + i-level do
text = text..""
end
print(text)
end
I want the result to be similar to the one in the picture.
Here is what your code looks like with proper formatting.
local level = 5
for i = 1, level do
local text = ""
for j = 1, i do
text = text..""
end
for j = 1, level-i, 1 do
text = text.." "
end
for j = 1+level, level+(level-i) do
text = text.." "
end
for j = 1, level + i-level do
text = text..""
end
print(text)
end
Your current code prints... well... an empty string. You haven't yet added the characters it's to display to be on par with the image.
The amount of characters per row is 9. So you ideally need 9 characters per row. You will also be incrementing the number once per row. The amount of characters per row also increases by 2; one on each side.
We can use the string.rep(string, number) function to duplicate a 'string' 'number' times. You can feed in your current level into that so it generates 1 2 or 3 depending on the line the number of times. Then you have whitespace to worry about. You can use string.rep again along with a bit of distance math to calculate the amount of whitespace you need from what you take up. Then finally throw everything together concatenated trailing with the first string and print.
local levels = 5
local columns = 9
for i=1, levels do
local str = string.rep(i, i)
local padding = columns - (#str * 2) + 1
print(str .. string.rep(" ", padding) .. str)
end

My ruby code is reliant on the number of the line appearing before the prompt. How do I fix this?

def main()
count = 1
while count <= 10
puts "#{count}" " Enter integer 1: "
int1=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 2: "
int2=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 3: "
int3=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 4: "
int4=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 5: "
int5=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 6: "
int6=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 7: "
int7=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 8: "
int8=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 9: "
int9=gets.chomp.to_i
count = count + 1
puts "#{count}" " Enter integer 10: "
int10=gets.chomp.to_i
count = count + 1
sum=int1+int2+int3+int4+int5+int6+int7+int8+int9+int10
end
puts "Total is: #{sum}"
end
main()
I made a program on ruby that prompts the user to enter a number 10 times and sums all the inputted numbers together. The only problem is that my code is reliant on the number to the corresponding line appearing before the line. For example, the first prompt is "1 Enter integer 1:" while what I'm aiming to make it is "Enter integer 1:".
Another issue I have is that I don't know how to make the input appear on the same line as the prompt. It appears on a new line, for example "1 Enter integer 1: while I want it to be "1 Enter integer 1: 156" 1"
Thank you.
The ruby REPL irb is line-at-a-time, so you can't get your input on the same line in the standard environment. (Other ways of doing it are of course available, up to and including a full Rails app! Which is far more than you want here, of course.)
To resolve your main issue, though, consider using an array to gather your input. You could then condense your code considerably, to something like this:
array = []
for i in 1..10 do
puts "Enter integer #{i}:"
array.push(gets.to_i)
end
sum = array.sum
If you don't need to prompt the user with a counter of the numbers they've entered, you could simplify even further like this:
array = []
10.times do
puts "Enter integer:"
array.push(gets.to_i)
end
sum = array.sum
sum = 0
(1..10).each do |i|
print "#{i} Enter integer: "
sum += gets.chomp.to_i
end
puts "Sum is " + sum.to_s
puts will add a newline to the output, print doesn't.

Using Regex and ruby regular expressions to find values

So I'm currently trying to sort values from a file. I'm stuck on the finding the first attribute, and am not sure why. I'm new to regex and ruby so I'm not sure how to go about the problem. I'm trying to find values of a,b,c,d,e where they are all positive numbers.
Here's what the line will look like
length=<a> begin=(<b>,<c>) end=(<d>,<e>)
Here's what I'm using to find the values
current_line = file.gets
if current_line == nil then return end
while current_line = file.gets do
if line =~ /length=<(\d+)> begin=((\d+),(\d+)) end=((\d+),(\d+))/
length, begin_x, begin_y, end_x, end_y = $1, $2, $3, $4, $5
puts("length:" + length.to_s + " begin:" + begin_x.to_s + "," + begin_y.to_s + " end:" + end_x.to_s + "," + end_y.to_s)
end
end
for some reason it never prints anything out, so I'm assuming it never finds a match
Sample input
length=4 begin=(0,0) end=(3,0)
A line with 0-4 decimals after 2 integers seperated by commas.
So it could be any of these:
2 4 1.3434324,3.543243,4.525324
1 2
18 3.3213,9.3233,1.12231,2.5435
7 9 2.2,1.899990
0 3 2.323
Here is your regex:
r = /length=<(\d+)> begin=((\d+),(\d+)) end=((\d+),(\d+))/
str.scan(r)
#=> nil
First, we need to escape the parenthesis:
r = /length=<(\d+)> begin=\((\d+),(\d+)\) end=\((\d+),(\d+)\)/
Next, add the missing < and > after "begin" and "end".
r = /length=<(\d+)> begin=\(<(\d+)>,<(\d+)>\) end=\(<(\d+)>,<(\d+)>\)/
Now let's try it:
str = "length=<4779> begin=(<21>,<47>) end=(<356>,<17>)"
but first, let's set the mood
str.scan(r)
#=> [["4779", "21", "47", "356", "17"]]
Success!
Lastly (though probably not necessary), we might replace the single spaces with \s+, which permits one or more spaces:
r = /length=<(\d+)>\s+begin=\(<(\d+)>,<(\d+)>\)\send=\(<(\d+)>,<(\d+)>\)/
Addendum
The OP has asked how this would be modified if some of the numeric values were floats. I do not understand precisely what has been requested, but the following could be modified as required. I've assumed all the numbers are non-negative. I've also illustrated one way to "build" a regex, using Regexp#new.
s1 = '<(\d+(?:\.\d+)?)>' # note single parens
#=> "<(\\d+(?:\\.\\d+)?)>"
s2 = "=\\(#{s1},#{s1}\\)"
#=> "=\\(<(\\d+(?:\\.\\d+)?)>,<(\\d+(?:\\.\\d+)?)>\\)"
r = Regexp.new("length=#{s1} begin#{s2} end#{s2}")
#=> /length=<(\d+(?:\.\d+)?)> begin=\(<(\d+(?:\.\d+)?)>,<(\d+(?:\.\d+)?)>\) end=\(<(\d+(?:\.\d+)?)>,<(\d+(?:\.\d+)?)>\)/
str = "length=<47.79> begin=(<21>,<4.7>) end=(<0.356>,<17.999>)"
str.scan(r)
#=> [["47.79", "21", "4.7", "0.356", "17.999"]]
Sample input:
length=4 begin=(0,0) end=(3,0)
data.txt:
length=3 begin=(0,0) end=(3,0)
length=4 begin=(0,1) end=(0,5)
length=2 begin=(1,3) end=(1,5)
Try this:
require 'pp'
Line = Struct.new(
:length,
:begin_x,
:begin_y,
:end_x,
:end_y,
)
lines = []
IO.foreach('data.txt') do |line|
numbers = []
line.scan(/\d+/) do |match|
numbers << match.to_i
end
lines << Line.new(*numbers)
end
pp lines
puts lines[-1].begin_x
--output:--
[#<struct Line length=3, begin_x=0, begin_y=0, end_x=3, end_y=0>,
#<struct Line length=4, begin_x=0, begin_y=1, end_x=0, end_y=5>,
#<struct Line length=2, begin_x=1, begin_y=3, end_x=1, end_y=5>]
1
With this data.txt:
2 4 1.3434324,3.543243,4.525324
1 2
18 3.3213,9.3233,1.12231,2.5435
7 9 2.2,1.899990
0 3 2.323
Try this:
require 'pp'
data = []
IO.foreach('data.txt') do |line|
pieces = line.split
csv_numbers = pieces[-1]
next if not csv_numbers.index('.') #skip the case where there are no floats on a line
floats = csv_numbers.split(',')
data << floats.map(&:to_f)
end
pp data
--output:--
[[1.3434324, 3.543243, 4.525324],
[3.3213, 9.3233, 1.12231, 2.5435],
[2.2, 1.89999],
[2.323]]

Printing 0 to 50 inclusive?

Why is this code not printing 0 to 50 inclusive?
i = 0
until i <= 50 do
print i
i += 1
end
Either use
until i > 50 do
# ...
end
or
while i <= 50 do
# ...
end
Here's a more "Ruby like" example:
(0..50).each do |i|
puts i
end
Ugh.
i = 0
until i <= 50 do
print i
i += 1
end
That would generate 51 iterations because you're starting at 0 and trying to run until 50 is reached, except that until is "notting" your condition. If you want to loop perhaps while would be a better test:
i = 0
while i <= 50 do
print i
i += 1
end
>> 01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950nil
But, even with while there are still 51 values being output:
i = 0
output = []
while i <= 50 do
output << i
i += 1
end
output.size # => 51
If you want to loop 50 times, why not use:
50.times do |i|
puts i
end
Or:
50.times { |i| puts i }
Change until to while. until is basically the same thing as while, but the conditional is inverted.
Another iterative method to use would be upto:
0.upto(50) do |i|
puts i
end
I really love this method for quick number iterations. It's super idiomatic (it does what it says) and it's inclusive of both start and end values so you don't have to calculate/account for an exclusive end val.
until stops executing when the condition it has is true. Because it is true from the beginning, nothing happens.
Just swap the comparison operators.
i = 0
until i > 50 do
print i
i += 1
end
You could also do
i = 0
while i <= 50 do
print i
i += 1
end
Use Idiomatic Ruby
Other answers have addressed why your original code doesn't work, and have pointed out the logic error in your conditional. However, it's worth noting that a more idiomatic way to do what you're doing would avoid the conditional altogether. For example:
(1..50).each { |i| pp i }
This works. ;)
i = 1
while i < 51 do
print i
i += 1
end
the first thing you have the comparison sing backwards
you want to do something like:
i = 0
until i >= 50 do
print i
i += 1
end
you can take a look at http://ruby-doc.org/core-2.1.2/doc/syntax/control_expressions_rdoc.html#label-until+Loop for more info

Ruby on Rails - Calculate Size of Number Range

Forgive my lack of code but I can't quite figure out the best way to achieve the following:
two strings (stored as strings because of the leading 0 - they are phone numbers) :
a = '0123456700'
b = '0123456750'
I am trying to find a way to write them as a range as follows
0123456700 - 750
rather than
0123456700 - 0123456750
which I currently have.
It's not as straightforward as getting the last 3 digits of b since the range can vary and perhaps go up to 4 digits so I'm trying to find the best way of being able to do this.
I'd look up the index of the first unequal pair of characters:
a = '0123456700'
b = '0123456750'
index = a.chars.zip(b.chars).index { |x, y| x != y }
#=> 8
And extract the suffix with:
"#{a} - #{b[index..-1]}" if index
#=> "0123456700 - 50"
Here's a method that returns the range:
def my_range(a, b)
a = a.delete(" ") # remove all spaces from string
b = b.delete(" ")
a, b = b, a if a.to_i > b.to_i # a is always smaller than b
ai, bi = a.to_i, b.to_i
pow = 1
while ai > 1
pow += 1
len = pow if ai % 10 != bi % 10
ai /= 10
bi /= 10
end
a + " - " + b[-len..-1]
end
puts my_range("0123456700", "0123456750") # 0123456700 - 750
puts my_range("0123456669", "0123456675") # 0123456669 - 675
puts my_range("0123400200", "0123500200") # 0123400200 - 3500200
puts my_range("012 345 678", "01 235 0521") # 012345678 - 350521
From my personal library (simplified):
def common_prefix first, second
i = 0
loop{break unless first[i] and second[i] == first[i]; i += 1}
first[0, i]
end
a = "0123456700"
b = "0123456750"
c = "0123457750"
common_prefix(a, b)
# => "01234567"
"#{a} - #{b.sub(common_prefix(a, b), "")}"
# => "0123456700 - 50"
"#{a} - #{c.sub(common_prefix(a, c), "")}"
# => "0123456700 - 7750"
Note. This will work correctly only under the assumption that all strings are right padded with 0 to be the same length.

Resources