Console table format in ruby - ruby

Is there a way to output an array of arrays as a table in ruby? I'm looking for something that is useful from the console, like powershell's format-table command.
An array that would be passed could be something like:
a = [[1.23, 5, :bagels], [3.14, 7, :gravel], [8.33, 11, :saturn]]
And the output could be something like:
-----------------------
| 1.23 | 5 | bagels |
| 3.14 | 7 | gravel |
| 8.33 | 11 | saturn |
-----------------------

I accepted sagarpandya82's answer, but here's my better implementation:
class Array
def to_table
column_sizes = self.reduce([]) do |lengths, row|
row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.length].max}
end
puts head = '-' * (column_sizes.inject(&:+) + (3 * column_sizes.count) + 1)
self.each do |row|
row = row.fill(nil, row.size..(column_sizes.size - 1))
row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.length)}
puts '| ' + row.join(' | ') + ' |'
end
puts head
end
end

You could try something like this:
a = [[1.23, 5, :bagels], [3.14, 7, :gravel], [8.33, 11, :saturn]]
bar = '-'*(a[0][0].to_s.length + 4 + a[0][1].to_s.length + 3 + a[0][2].to_s.length + 4)
puts bar
a.each do |i|
i.each.with_index do |j,k|
if k == 1 && j < 10
print "| #{j} "
else
print "| #{j} "
end
end
print '|'
puts
end
puts bar
returns:
----------------------
| 1.23 | 5 | bagels |
| 3.14 | 7 | gravel |
| 8.33 | 11 | saturn |
----------------------
bar is just an estimate of how long the top and bottom dash-bar will be. In general this code checks each sub-array and prints out its elements with | in the appropriate places. The only tricky bit is the second element of each sub-array. Here we check to see if it is double digit or single digit. The print-format is different for each.
Bear in mind that this code works specifically for your example and makes a lot of assumptions. Having said that it can be easily modified to taste.

I've made a small improvement on the #dart-egregious snippet
class Array
def to_table(header: true)
column_sizes = self.reduce([]) do |lengths, row|
row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.length].max}
end
head = '+' + column_sizes.map{|column_size| '-' * (column_size + 2) }.join('+') + '+'
puts head
to_print = self.clone
if (header == true)
header = to_print.shift
print_table_data_row(column_sizes, header)
puts head
end
to_print.each{ |row| print_table_data_row(column_sizes, row) }
puts head
end
private
def print_table_data_row(column_sizes, row)
row = row.fill(nil, row.size..(column_sizes.size - 1))
row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.length)}
puts '| ' + row.join(' | ') + ' |'
end
end
Using:
data = [
['column 1', 'column 2', 'ciolumn 3'],
['row 1 col 2', 'row 1 col 2', 'row 1 col 3'],
['row 1 col 2', 'row 1 col 2', 'row 1 col 3']
]
data.to_table
+-------------+-------------+-------------+
| column 1 | column 2 | ciolumn 3 |
+-------------+-------------+-------------+
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
+-------------+-------------+-------------+
data.to_table(header: false)
+-------------+-------------+-------------+
| column 1 | column 2 | ciolumn 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
+-------------+-------------+-------------+

Related

noMethodError on a < symbol ruby

enter image description here
The link for the error is above; I keep getting a noMethodError for a symbol and I am very confused
numList = [4,3,78,2,0,2]
def bubbleSort(list)
again = true
while again
copy = list
i = 0
until i == list.length do
list.each_with_index do |num, index|
if list[index+1] < num
tempNum = list[index+1]
list[index+1] = num
list[index] = tempNum
end
end
i = i+1
end
if copy != list
again = true
else
again = false
end
p copy
end
end
bubbleSort(numList)
As noted in comments, you are iterating with an index:
list.each_with_index do |num, index|
But this includes the last index, which you're trying to access one past.
if list[index+1] < num
list[index+1] return nil, which will give the error you're seeing.
irb(main):083:0> nil < 42
Traceback (most recent call last):
5: from C:/Ruby26-x64/bin/irb.cmd:31:in `<main>'
4: from C:/Ruby26-x64/bin/irb.cmd:31:in `load'
3: from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):83
1: from (irb):83:in `rescue in irb_binding'
NoMethodError (undefined method `<' for nil:NilClass)
It'd be important to avoid this access. I would also suggest avoiding the again boolean flag. Rather use loop and break out of the loop at the appropriate time.
def bubbleSort(list)
loop do
copy = list
i = 0
until i == list.length
list.each_with_index do |num, index|
next if index == list.length - 1
if list[index+1] < num
tempNum = list[index+1]
list[index+1] = num
list[index] = tempNum
end
end
i = i+1
end
break if copy == list
p copy
end
end
Taking a step back and looking at how bubble sort works though, your implementation can be improved.
Each cycle through the array a bubble sort will advance the largest element to the end of the array. You don't need to cycle over the whole array each times, but can decrease the inner loop by one iteration each time.
You can also track the number of swaps. If no swaps are performed, the array is clearly sorted already, and the outer loop can be exited.
+---+---+---+---+
| 4 | 2 | 3 | 1 | (starting state for loop)
+---+---+---+---+
>---<
+---+---+---+---+
| 2 | 4 | 3 | 1 | (swap 4 and 2)
+---+---+---+---+
>---<
+---+---+---+---+
| 2 | 3 | 4 | 1 | (swap 4 and 3)
+---+---+---+---+
>---<
+---+---+---+---+
| 2 | 3 | 1 | 4 | (swap 4 and 1)
+---+---+---+---+
Three swaps are performed. The last position in the array is sorted.
+---+---+---+---+
| 2 | 3 | 1 | 4 | (starting state for loop)
+---+---+---+---+
>---<
+---+---+---+---+
| 2 | 1 | 3 | 4 | (swap 3 and 1)
+---+---+---+---+
+---+---+---+---+
| 2 | 1 | 3 | 4 | (no swap needed for 1 and 3)
+---+---+---+---+
One swap is performed. Now the last two positions are sorted.
+---+---+---+---+
| 2 | 1 | 3 | 4 | (starting state for loop)
+---+---+---+---+
>---<
+---+---+---+---+
| 1 | 2 | 3 | 4 | (swap 2 and 1)
+---+---+---+---+
One swap is performed. We know the last three positions are sorted. The first position therefore is sorted.
If the array is already sorted and we keep track of swaps:
+---+---+---+---+
| 1 | 2 | 3 | 4 | (starting state for loop)
+---+---+---+---+
+---+---+---+---+
| 1 | 2 | 3 | 4 | (no swap needed for 1 and 2)
+---+---+---+---+
+---+---+---+---+
| 1 | 2 | 3 | 4 | (no swap needed for 2 and 3)
+---+---+---+---+
+---+---+---+---+
| 1 | 2 | 3 | 4 | (no swap needed for 3 and 4)
+---+---+---+---+
Since zero swaps are performed, the array is known to be sorted. While bubble sort is worst case O(n^2), this technique can make it best case O(n).
def bubble_sort(arr)
(arr.length-1).times do |x|
swaps = 0
0.upto(arr.length-x-2) do |i|
if arr[i] > arr[i+1]
temp = arr[i+1]
arr[i+1] = arr[i]
arr[i] = temp
swaps += 1
end
end
break if swaps == 0
end
arr
end

How to draw a square grid of size n to console?

I need to draw a square grid of size n to the console. The grid uses - for horizontal cell boundaries, | for vertical cell boundaries, and + for corners of each cell.
As an example, a size 3 grid should look like the following:
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
I was thinking of using a double for loop with outer loop iterating through rows and inner loop iterating through cols. each iteration of inner loop would be processing an individual cell. drawing | characters doesn't seem to hard but I'm not sure how I'd go about printing the - chars above and below a cell.
You could use Integer#times and String#*:
def print_grid(n)
n.times { print "+-"*n, "+\n", "| "*n, "|\n" }
print "+-"*n, "+\n"
end
print_grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
=> nil
Alternatively:
def print_grid(n)
puts n.times.map{ "+-"*n + "+\n" + "| "*n + "|\n" }.join + "+-"*n + "+\n"
end
For a width of 3, you have a separator-row like this:
+-+-+-+
This can be sees as either:
3 - connected by and surrounded by +
4 + connected by -
The latter is a bit easier to express in Ruby:
width = 3
Array.new(width + 1, '+').join('-')
#=> "+-+-+-+"
The same works for the cell-row:
Array.new(width + 1, '|').join(' ')
#=> "| | | |"
Vertically, you have 3 cell-rows connected by and surrounded by separator-rows. (that should ring a bell) Just like before, this can also be expressed as 4 separator-rows connected by cell-rows.
Let's store our separator-row and cell-row in variables: (we also have to append newlines)
width = 3
separator_row = Array.new(width + 1, '+').join('-') << "\n"
cell_row = Array.new(width + 1, '|').join(' ') << "\n"
And define the grid:
height = 3
grid = Array.new(height + 1, separator_row).join(cell_row)
#=> "+-+-+-+\n| | | |\n+-+-+-+\n| | | |\n+-+-+-+\n| | | |\n+-+-+-+\n"
put grid
Output:
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
Here are two ways to do that.
Determine each character depending on whether the row and column indices are even or odd
def grid(n)
sz = 2*n+1
Array.new(sz) do |i|
Array.new(sz) do |j|
if i.even?
j.even? ? '+' : '-'
else # i is odd
j.even? ? '|' : ' '
end
end.join
end
end
puts grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
puts grid(4)
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
Use an enumerator
def pr_grid(n)
enum = [*['+','-']*n, "+", "\n", *['|',' ']*n, "|", "\n"].cycle
((2+2*n)*(1+2*n)).times { print enum.next }
end
pr_grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
pr_grid(4)
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
For n = 3 the steps are as follows.
a = [*['+','-']*n, "+", "\n", *['|',' ']*n, "|", "\n"]
#=> ["+", "-", "+", "-", "+", "-", "+", "\n",
# "|", " ", "|", " ", "|", " ", "|", "\n"]
enum = a.cycle
#=> #<Enumerator: ["+", "-", "+", "-", "+", "-", "+", "\n",
# "|", " ", "|", " ", "|", " ", "|", "\n"]:cycle
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "\n"
enum.next #=> "|"
enum.next #=> " "
and so on.
Just for fun and just as an example, here is a list of methods (to be optimized and debugged and maybe used within a class) that you can use also for filling a table.
#cell = '|'
#line = '-'
#cross = '+'
def build_row(content)
(content.zip [#cell]* content.size).flatten.prepend(#cell).join
end
def separator_from_content(content)
content.map { |e| #line * e.size + #cross }.prepend(#cross).join
end
def adjust_content_in(lines)
width = lines.flatten.max_by(&:size).size
lines.map { |line| line.map { |e| e << ' ' * (width - e.size) } }
end
def build_table(lines)
lines = adjust_content_in(lines)
separator = separator_from_content(lines.first)
mapped = lines.map { |line| build_row(line) }.zip([separator] * (lines.size) )
return mapped.flatten.prepend(separator).join("\n")
end
def empty_squared_table(n)
lines = n.times.map { n.times.map { ' ' } }
build_table(lines)
end
So, if you want to draw an empty table, can call:
n = 3
puts empty_squared_table(n)
# +-+-+-+
# | | | |
# +-+-+-+
# | | | |
# +-+-+-+
# | | | |
# +-+-+-+
Or you can fill in some text (more lines inside a cell not supported):
lines = [['so', 'you can', 'fill'], ['a table', 'given the content', '']]
puts build_table(lines)
# +-----------------+-----------------+-----------------+
# |so |you can |fill |
# +-----------------+-----------------+-----------------+
# |a table |given the content| |
# +-----------------+-----------------+-----------------+

No for loops in elixir. How can I make a board?

Here is a snippet of the for loop so far. The problem is decrementing spaces variable
for i <- 0..(row-1) do
for j <- 0..(col-1) do
if j == 0 do
IO.write"+"
end
IO.write "--+"
end
IO.puts ""
columns= col+1
for columns <- 1..columns do
if columns == 1 do
IO.write "|"
end
if spaces < 10 do
IO.write " "
IO.write spaces
IO.write "|"
end
end
spaces = spaces - 1 <- This won't decrement after it goes through the forloop again!
end
end
I need to make a board game in Elixir that outputs something like this.
+---+---+---+
| 12| 11| 10|
|B | | |
+---+---+---+
| 7| 8| 9|
| a | S| e |
+---+---+---+
| 6| 5| 4|
| e | L|Ad |
+---+---+---+
| 1| 2| 3|
| | | |
+---+---+---+
I've done it on java with the following code using a for loop within another for loop essentially, but this is not possible on elixir. Is there any idea on how to approach this? Thanks.
EDIT: in case anyone wants to see my java code, here it is.
public class math {
static int col = 10;
static int row = 10;
static String top= "---+";
static String side= "|";
static int spaces=col*row;
static int columns;
static int dash;
static String player="A";
public static void main(String args[]){
for ( int i=0; i < row; i++ ){
for ( int j=0; j < col; j++ ){
if ( j==0) System.out.print("+");
System.out.print(top);
}
System.out.print("\n");
columns=col+1;
while( columns > 1){
if(columns == col+1){
System.out.print("|");
}
if( spaces < 10){
System.out.print(" "+spaces+"|");}
else if (spaces <100){
System.out.print(" "+spaces+"|");
}
else{
System.out.print(""+spaces+"|");
}
spaces--;
columns--;}
System.out.print("\n");
columns=col+1;
while(columns >1){
if (columns == col +1) {System.out.print("|");}
System.out.print(" |");
columns--;}
System.out.print("\n");
if( i == row-1){
int botside = col;
while(botside !=0){
if(botside == col){
System.out.print("+");
}
System.out.print(top);
botside--;}
}
}
}
You could use the for special form for list comprehension to achieve what you need with something like:
for i <- 0..(row-1) do
for j <- 0..(col-1) do
# Rest of the code
end
end
Here is my solution - based on a board template that you fill with your data.
defmodule Main do
def main( ) do
data =
[
"B ", " ", " ",
" a ", " S", " e ",
" e ", " L", "Ad ",
" ", " ", " ",
"" #required terminal
]
IO.puts print_board( data )
end
defp print_board( data ) do
board = """
+---+---+---+
| 12| 11| 10|
| | | |
+---+---+---+
| 7| 8| 9|
| | | |
+---+---+---+
| 6| 5| 4|
| | | |
+---+---+---+
| 1| 2| 3|
| | | |
+---+---+---+
"""
String.split( board," ") # Splits board string into a list of strings
|> Enum.zip( data ) # interleaves with data - as list of tuples
|> Enum.map( fn {a,b} -> a <> b end ) # maps tuples back to merge the list.
end
end
My testing shows it produces your required result when you run Main.main

Infinite loop nested within a block in Ruby

Here's what I'm trying to do. I'm iterating through an array of strings, each of which may contain [] multiple times. I want to use String#match as many times as necessary to process every occurrence. So I have an Array#each block, and nested within that is an infinite loop that should break only when I run out of matches for a given string.
def follow(c, dir)
case c
when "\\"
dir.rotate!
when "/"
dir.rotate!.map! {|x| -x}
end
return dir
end
def parse(ar)
ar.each_with_index { |s, i|
idx = 0
while true do
t = s.match(/\[[ ]*([a-zA-Z]+)[ ]*\]/) { |m|
idx = m.end 0
r = m[1]
s[m.begin(0)...idx] = " "*m[0].size
a = [i, idx]
dir = [0, 1]
c = ar[a[0]][a[1]]
while !c.nil? do
dir = follow c, dir
ar[a[0]][a[1]] = " "
a[0] += dir[0]; a[1] += dir[1]
c = ar[a[0]][a[1]]
if c == ">" then
ar[a[0]][a[1]+1] = r; c=nil
elsif c == "<" then
ar[a[0]][a[1]-r.size] = r; c=nil
end
end
ar[a[0]][a[1]] = " "
puts ar
}
if t == nil then break; end
end
}
parse File.new("test", "r").to_a
Contents of test:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
/--> /----> | |
| |[kale]/ | hot dogs | Brazil |
| | <----------------------\ |
| | orange |[yellow]\ | [green]/ |
| +--------+--------|-+-------------+
\-------------------/
Goal:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
yellow kale | |
| | hot dogs | Brazil |
| green |
| orange | | |
+--------+-------- -+-------------+
Actual output of program:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
yellow kale | |
| | hot dogs | Brazil |
| <----------------------\ |
| orange | | [green]/ |
+--------+-------- -+-------------+
(Since the array is modified in place, I figure there is no need to update the match index.) The inner loop should run again for each pair of [] I find, but instead I only get one turn per array entry. (It seems that the t=match... bit isn't the problem, but I could be wrong.) How can I fix this?

How do I print a hash of hash to a table in an ASCII file?

I am trying to print a hash of hash to a table in a log file in ASCII format. I have a hash, where its values are again a hash and its value is a list:
irb(main):057:0> h = {
irb(main):058:1* 'mode1' => {
irb(main):059:2* 'name1' => [2, 4],
irb(main):060:2* 'name2' => [54, 65]
irb(main):061:2> },
irb(main):062:1* 'mode2' => {
irb(main):063:2* 'name4' => [3, 0],
irb(main):064:2* 'name3' => [2, 1]
irb(main):065:2> },
irb(main):066:1* 'mode3' => {
irb(main):067:2* 'xys' => [100, 28]
irb(main):068:2> }
irb(main):069:1> }
I want it to be printed as:
**************************************************************************************
| No | Mode Name | Sub Name | Value 1 | Value 2 |
**************************************************************************************
| 1 | mode1 | name1 | 2 | 4 |
| | | name2 | 54 | 65 |
--------------------------------------------------------------------------------------
| 2 | mode2 | name4 | 3 | 0 |
| | | name3 | 2 | 1 |
--------------------------------------------------------------------------------------
| 3 | mode3 | xys | 100 | 28 |
**************************************************************************************
Is there an easy way to achieve this?
I am trying out my own way using printf, but I am not able to figure out how to center-justify, using a hash.each block and I am stuck when the inner hash has multiple keys. I don't need to print first and second columns.
I am using ruby 1.8.6 (2009-08-04 patchlevel 383).
Based on "What is the “right” way to iterate through an array in Ruby?":
puts "*" * <number of asterisks>
puts "|\tNo\t|\tMode\t|..."
puts "*" * <number of asterisks>
number = 1
for mode in h.keys.sort
first = true
for subkey in mode.keys.sort
if first
puts "|\t#{number.inspect}\t|\t#{h[mode].inspect}\t|\t#{subkey.inspect}\t|..."
first = false
else
puts "|\t\t|\t\t|\t#{subkey.inspect}\t|..."
end
puts "-" * <number of dashes>
end
number += 1
end

Resources