array = Array.new(10) { Array.new(10 , 0)}
array.each { |x| print x }
Prints out one single line of ten [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
If I were to change print to puts, I then get 100 0 down the page.
How do I print out each array on a separate line without the "[]" and ","?
Something like:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
Suppose:
arr = Array.new(10) { (0..20).to_a.sample(10) }
Then
puts arr.map { |x| x.join(' ') }
1 9 6 15 7 19 18 3 0 12
13 20 18 15 0 3 19 1 14 16
7 16 5 3 12 19 4 9 20 10
6 10 9 1 18 17 7 19 5 15
12 3 8 16 10 5 2 18 20 6
12 9 0 18 2 11 16 8 7 15
8 9 14 19 3 16 6 20 13 17
7 19 16 14 13 6 9 2 3 5
10 17 8 15 11 2 13 14 16 7
14 9 20 17 15 3 4 2 11 19
is not very, er, attractive. For something more pleasing, you could quite easily do something like this:
width = arr.flatten.max.to_s.size+2
#=> 4
puts arr.map { |a| a.map { |i| i.to_s.rjust(width) }.join }
1 9 6 15 7 19 18 3 0 12
13 20 18 15 0 3 19 1 14 16
7 16 5 3 12 19 4 9 20 10
6 10 9 1 18 17 7 19 5 15
12 3 8 16 10 5 2 18 20 6
12 9 0 18 2 11 16 8 7 15
8 9 14 19 3 16 6 20 13 17
7 19 16 14 13 6 9 2 3 5
10 17 8 15 11 2 13 14 16 7
14 9 20 17 15 3 4 2 11 19
If you have too many columns to display on the screen you can do this:
puts arr.map { |a| a.map { |i| i.to_s.rjust(width) }.join.tinyfy }
1 9 6 15 7 19 18 3 0 12
13 20 18 15 0 3 19 1 14 16
7 16 5 3 12 19 4 9 20 10
6 10 9 1 18 17 7 19 5 15
12 3 8 16 10 5 2 18 20 6
12 9 0 18 2 11 16 8 7 15
8 9 14 19 3 16 6 20 13 17
7 19 16 14 13 6 9 2 3 5
10 17 8 15 11 2 13 14 16 7
14 9 20 17 15 3 4 2 11 19
Try join:
array.each { |x|
puts x.join(" ")
}
# prints:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
Print a 2-dimensional array with spacing between columns
Accepts a 2d array of objects that have a to_s method (strings integers floats and booleans..) and an optional margin width integer.
Update: It now works with arrays of varying lengths.
def print_table(table, margin_width = 2)
# the margin_width is the spaces between columns (use at least 1)
column_widths = []
table.each do |row|
row.each.with_index do |cell, column_num|
column_widths[column_num] = [column_widths[column_num] || 0, cell.to_s.size].max
end
end
puts (table.collect do |row|
row.collect.with_index do |cell, column_num|
cell.to_s.ljust(column_widths[column_num] + margin_width)
end.join
end)
end
Note: the parenthesis after the puts statement is required so table.collect and the do end block are not passed as two separate parameters to the puts method.
Example table
my_table = [
["1", "Animal", "Dog", "1"],
[1, "Animal", "Cat", "2"],
[1, "Animal", "Bird", "3"],
[2, "Place", "USA", "1"],
[2.5, "Place", "Other", "2"],
[3, "Color", "Red"],
[3, "Color", "Blue", "b"],
[3, "Some more color", "Orange", "c"],
[4.7, "Age", "Young", "a"],
[4, "Age", "Middle", "b"],
[4, "Age", "Old", "c"],
[5, "Alive"],
[],
[5, "Alive", false, "n"]
]
print_table my_table
Prints:
1 Animal Dog 1
1 Animal Cat 2
1 Animal Bird 3
2 Place USA 1
2.5 Place Other 2
3 Color Red
3 Color Blue b
3 Some more color Orange c
4.7 Age Young a
4 Age Middle b
4 Age Old c
5 Alive
5 Alive false n
(Not colored. The coloring above was added by StackOverflow.)
You may want to write your own method to do it. Something like:
def array_2D_print array
array.each do |arr|
arr.each do |item|
print "#{item} "
end
print "\n"
end
end
If you only use this once in your code, you might also consider not creating any method:
array.each do |arr|
arr.each do |item|
print "#{item} "
end
print "\n"
end
This solution has the advantage of being easier to modify than other alternatives, to match what you want to print.
Related
Newbie here! Trying to implement a program to print the first 20 Fibonacci numbers in Ruby. I've managed to create a program which generates the nth number, but I want to produce all from 0 through to 20.
Is there a simple way to do this or do I need to rewrite the whole program?
CURRENT CODE
def fib(n)
if n < 1
return 0
elsif n == 1
return 1
else fib(n-2) + fib(n-1)
end
end
puts fib(20)
CURRENT OUTPUT EXAMPLE
6765
DESIRED OUTCOME
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
At the moment you only print the last value returned by your method (fib(20)) but not the result of all intermediate steps.
An easy way would be to cache all intermediate results in a hash data structure. This would also improve performance for big n because you do not need to recalculate many values over and over again.
Then you can just print out all results from 0 to n:
def cached_fib(n)
#cache ||= Hash.new do |cache, n|
#cache[n] = n < 2 ? n : cache[n-1] + cache[n-2]
end
#cache[n]
end
def fib(n)
0.upto(n) { |i| puts cached_fib(i) }
end
fib(20)
#=> 0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
Printing each value is easier with a button-up approach where you start at 0 and 1 and calculate each following value based on its predecessors, e.g.:
i, j = 0, 1
puts i
puts j
21.times do
k = i + j
puts k
i, j = j, k
end
You could turn the above into an Enumerator:
fib = Enumerator.new do |y|
i, j = 0, 1
y << i
y << j
loop do
k = i + j
y << k
i, j = j, k
end
end
Which will generate the sequence:
fib.take(21)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
# 233, 377, 610, 987, 1597, 2584, 4181, 6765]
This instructions are as follow:
NUMBER CRUNCHER
Write a method that takes a number as an argument
If the number is greater than 20
count down from the number by 2's
If the number is less than 20
count down from the number by 1's
Display the numbers as they count down to 0.
I have written this, but it's not doing what it's supposed. Any help?
def num_cruncher(num)
count = num
until count == 0 do
if num > 20
puts count - 2
else
puts "#{count}"
end
count -= 1
end
end
Here's your code, with as few changes as possible :
def num_cruncher(num)
count = num
until count < 0 do
puts count
if num > 20
count -= 2
else
count -= 1
end
end
end
num_cruncher(10)
# 10
# 9
# 8
# 7
# 6
# 5
# 4
# 3
# 2
# 1
# 0
num_cruncher(21)
# 21
# 19
# 17
# 15
# 13
# 11
# 9
# 7
# 5
# 3
# 1
By extracting the if-statement outside of the loop, the code becomes a bit shorter :
def num_cruncher(num)
if num > 20
step = 2
else
step = 1
end
until num < 0 do
puts num
num -= step
end
end
You can use Numeric#step here. Something like this:
def num_cruncher n
s = n > 20 ? -2 : -1
n.step(by: s, to: 0).entries
end
num_cruncher 23
#=> [23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1]
I have a 2d map which is an array of arrays:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
I also have a list with moves:
moves = [[0,0], [0, 1], [1, 1]]
I want to print the movement on console (but I want every time to overwrite previous output, like this)
So the expected output should be something like this
* 0 0 0 0 0 * 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 * 0 0 0
0 1 1 1 1 --> 0 1 1 1 1 --> 0 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I have tried some things but I can't get close to my desired output.
To clear the screen and wait for ENTER, try this:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
loc = [0,0]
moves = [[0,0], [0, 1], [1, 1]]
def display_map(map,loc)
system 'clear'
loc.first.times { puts }
map.each { |row| print ' '*loc.last; p row }
end
moves.each do |x,y|
loc[0] += x
loc[1] += y
display_map(map,loc)
gets
end
This works on a Mac. For other OS's you may have to replace system 'clear' with system 'cls'.
[Edit: I see I misunderstood the question. I think this is what you want:
moves.each do |x,y|
system 'clear'
nrows.times do |i|
ncols.times do |j|
print (i==x && j==y) ? '*' : map[i][j]
print ' ' if j < ncols-1
end
puts
end
gets
end
You can use ANSI terminal escape codes.
Example:
# Save initial cursor position
puts "\033[s"
puts <<EOF
* 0 0 0 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
sleep 1
# Restore initial cursor position
puts "\033[u"
puts <<EOF
0 * 0 0 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
sleep 1
# Restore initial cursor position
puts "\033[u"
puts <<EOF
0 0 0 0 0
0 * 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
Following #Cary_Swoveland's solution with clearing the console, I manage to do it like this:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
moves = [[0,0], [0, 1], [1, 1]]
def display_map(map,loc)
system "clear" or system "cls"
# loc.first.times { puts }
map.each { |row| p row }
end
moves.each do |x,y|
map[x][y] = 8
display_map(m,loc)
map[x][y] = 0
gets
end
So I have this code:
def self.age_to_bucket(age)
age = age.to_i
if age >= 0 && age <= 12
1
elsif age >= 13 && age <= 17
2
elsif age >= 18 && age <= 24
3
elsif age >= 25 && age <= 29
4
elsif age >= 30 && age <= 34
5
elsif age >= 35 && age <= 39
6
elsif age >= 40 && age <= 49
7
elsif age >= 50 && age <= 64
8
elsif age >= 65
9
else
0
end
end
How can I improve this code without losing its readability?
I know I can use #in? with ranges, like this:
if age.in? (0..12)
but #in? is in ActiveSupport, and I'd rather use more independent way.
One way is to use case
result = case age
when 0..12 then 1
when 13..17 then 2
when 18..24 then 3
when 25..29 then 4
-------- so on
else 0
end
Another way would be to eliminate the redundant && in the condition.
if age < 0
0
elsif age < 13
1
elsif age < 18
2
elsif age < 25
3
elsif age < 30
4
elsif age < 35
5
elsif age < 40
6
elsif age < 50
7
elsif age < 65
8
else
9
def self.age_to_bucket age
case age=age.to_i
when 0..12 then 1
when 13..17 then 2
when 18..24 then 3
when 25..29 then 4
when 30..34 then 5
when 35..39 then 6
when 40..49 then 7
when 50..64 then 8
else age >= 65 ? 9 : 0
end
end
You can rewrite if age.in? (0..12) to (0..12).include? age, which is vanilla Ruby.
Just for fun (this is not the efficient way, but for small arrays is just fine):
ranges = [0, 13, 18, 25, 30, 35, 40, 50, 65, Float::INFINITY].each_cons(2).map { |a, b| (a..b) }
n = ranges.map.with_index { |range, idx| idx if range.include?(15) }.compact.first + 1
#=> 2
Note that if the intervals were dynamic you'd have to implement it in a similar fashion.
irb(main):010:0> a = {1 => 0..12, 2 => 13..17} # insert other values here
=> {1=>0..12, 2=>13..17}
irb(main):011:0> age = 16
=> 16
irb(main):012:0> a.keys.find {|k| a[k].include?(age) }
=> 2
I need to find the averages for all columns in the same row of a particular value of the first column. So for a table like the following:
0 11 12 40
1 22 24 92
0 12 13 45
1 24 26 90
2 33 36 138
1 22 24 80
2 36 39 135
0 11 12 46
2 33 36 120
I want the resulting table of averages:
0 11.33333333 12.33333333 43.66666667
1 22.66666667 24.66666667 87.33333333
2 34 37 131
So for example for the value 0..
I take the averages of (11 + 12 + 11)/3 on the second column, (12 + 13 + 12)/3 on the second, and (46 + 45 + 40)/3 on the third column
a = [
[0, 11, 12, 40],
[1, 22, 24, 92],
[0, 12, 13, 45],
[1, 24, 26, 90],
[2, 33, 36, 138],
...
]
def average array, index
array = array.select{|l| l[0] == index}
n = array.length.to_f
array.transpose.drop(1).map {|values| values.inject(0){|s, v| s += v}/n}
end
average(a, 0) # => [11.33333333, 12.33333333, 43.66666667]
To have all of them at once:
array.group_by{|l| l[0]}.map do |k, array|
n = array.length.to_f
[k, array.transpose.drop(1).map {|values| values.inject(0){|s, v| s += v}/n}]
end
# =>
[
[0, [11.33333333, 12.33333333, 43.66666667]],
[1, [22.66666667, 24.66666667, 87.33333333]]
[2, [34.0, 37.0, 131.0]]
]
data = "0 11 12 40
1 22 24 92
0 12 13 45
1 24 26 90
2 33 36 138
1 22 24 80
2 36 39 135
0 11 12 46
2 33 36 120"
avgs = data.split(/\n/).map{|d| d.split(/\t/)}.group_by{|d| d[0] }.each{|a,b| b.each(&:shift) }.inject({}){|avg, (k, ar)| avg[k] = ar.inject([0,0,0]){|av,(a,b,c)| av[0]+=a.to_f; av[1]+=b.to_f; av[2]+=c.to_f; av}.map{|e| e/ar.size}; avg}
#=> {"0"=>[11.333333333333332, 12.333333333333332, 43.66666666666667], "1"=>[22.666666666666664, 24.666666666666664, 87.33333333333334], "2"=>[34.0, 37.0, 131.0]}
to print it:
avgs.each{|k, arr| puts [k,*arr].join("\t") }
#=> 0 11.333333333333332 12.333333333333332 43.66666666666667
#=> 1 22.666666666666664 24.666666666666664 87.33333333333334
#=> 2 34.0 37.0 131.0
UPD
I've cleaned my method a little:
avgs = data.split(/\n/).
map{|d| d.split(/\t/).map(&:to_f)}.
group_by(&:first).
inject({}){|avg, (k, ar)| avg[k] = ar.transpose[1..-1].map{|av| av.inject(:+)/av.size}; avg}
say you have a table table = [ [0, 11, 12, 40] , [1, 22, 24, 92] .... [2, 33, 36, 120] ]
i'm presuming the table has a fixed number of elements on a line
first_elements_array = table.map { |line| line[0] }.uniq! #get [0,1,2] from your table
avgs = [] #will contain your [ [0, 11.333, 12.3333, 43.66667], ... ]
for element in first_elements_array
#i need an array with 4 zeros - the number of elements on a line
temp_sum = [0,0,0,0]
count = 0 #number of lines that start with 0, or 1, or 2 etc
#read the lines that start with 0, then 1, then 2 etc
for line in table
if line[0] == element
count += 1
#add in temp_sum the new line found, element by element
(0...line.length).each do |i|
temp_sum[i] += line[i]
end
end
end
line_avg = []
#so, temp_sum contains the sum for one line that starts with 0 or 1 or 2 etc. now calculate the average
for sum in temp_sum
line_avg << sum/count
end
#... and push it in an array
avgs << line_avg
end
this could probably be done more elegant so feel free to adapt it
also, haven't had any time to test it, let me know if it works