Creating matrix with `Array.new(n, Array.new)` - ruby

I created an array by doing the following:
#gameboard = Array.new(3, Array.new(3, " "))
I tried to assign a value like so, and I got this:
#gameboard[0][2] = "X"
#gameboard #=> [[" ", " ", "X"], [" ", " ", "X"], [" ", " ", "X"]]
When I declare the array differently,
#gameboard = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]
I get this result:
#gameboard[0][2] = "X"
#gameboard # => [[" ", " ", "X"], [" ", " ", " "], [" ", " ", " "]]
Why does using the Array.new method illicit different behavior when assigning values to the array?

Follow the code:
#gameboard = Array.new(3, Array.new(3, " "))
#gameboard.map { |a| a.object_id }
# => [76584030, 76584030, 76584030]
means new(size=0, obj=nil) method creates an array of size, having the same ob.
But new(size) {|index| block } method works in a different way; it creates an array of size, having different obs.
See the code below:
#gameboard = Array.new(3) { Array.new(3, " ") }
#gameboard.map { |a| a.object_id }
# => [75510080, 75509920, 75509540]
The above is the same as your second code example:
#gameboard = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]
#gameboard.map { |a| a.object_id }
# => [80194090, 80193400, 80193080]
If you change or update the the value at index 1 of the first element array of #gameboard, it wouldn't affect all other inner array elements.
#gameboard = Array.new(3) { Array.new(3, " ") }
#gameboard[0][1] = 2
#gameboard
# => [[" ", 2, " "], [" ", " ", " "], [" ", " ", " "]]

The Array constructor will not duplicate the object you passed; it will reuse the object to fill the array.
Use the block form in order to create a new object for each index:
#gameboard = Array.new(3) { |i| Array.new(3) { |j| " " } }

Related

How to remove quotes from strings in an array?

Assuming I have the following input:
names = ["\"Петр Сергеевич\"", "\"Курсатов Роман\"", "\" \"", "\"Павел2 Олегович\"", "\"Илья иванович\"", "\" \""]
Each whitespace is actually a non-breaking space (U+00A0).
How do I remove \" in pure ruby, so the following is true:
p names
=> ["Петр Сергеевич", "Курсатов Роман", " ", "Павел2 Олегович", "Илья иванович", " "]
I tried:
names.map { |i| i.gsub(/[\"]/, "")}.map(&:inspect)
names.map { |i| i.delete('\\"')}.map(&:inspect)
names.map { |i| i.gsub('\\"', '')}.map(&:inspect)
Nothing seems to work.
string.delete("\"")
# => " "
or
string.tr("\"", "")
# => " "

Ruby cycle to alternate table row colors getting duplicates

I am trying to alternate colors every row but I am getting duplicate rows
def output_restaurant_table(restaurants=[])
print " " + "Name".ljust(30)
print " " + "Cuisine".ljust(20)
print " " + "Price".rjust(26) + "\n"
puts "-" * 80
restaurants.each do |rest|
line = " " << rest.name.titleize.ljust(30)
line << " " + rest.cuisine.titleize.ljust(20)
line << " " + rest.formatted_price.rjust(26)
x = [:red, :white]
x.cycle(1) { |x| output_action_header(line, :black, x) }
end
puts "No listings found" if restaurants.empty?
puts "-" * 80
end
If I try to move it out
If I had to guess - it's restarting the cycle every time you do another restaurant because of that line.
Try moving the x = [:red, :white] line outside of the restaurants.each loop (above it).
def output_restaurant_table(restaurants=[])
print " " + "Name".ljust(30)
print " " + "Cuisine".ljust(20)
print " " + "Price".rjust(26) + "\n"
puts "-" * 80
# this is where you instantiate the set of things that will be cycled through.
# You don't want to do this for every loop or the cycle will start over
# every time you go through the loop (starting at the beginning every time)
x = [:red, :white].cycle
restaurants.each do |rest|
line = " " << rest.name.titleize.ljust(30)
line << " " + rest.cuisine.titleize.ljust(20)
line << " " + rest.formatted_price.rjust(26)
# this is where you are saying "get me the next value of this cycle"
x.next { |x| output_action_header(line, :black, x) }
end
puts "No listings found" if restaurants.empty?
puts "-" * 80
end

Replacing "\n" but not "\n\n"

How to replace "\n" but not "\n\n" etc. with " \n"?
text1 = "Hello\nWorld"
text1.sub! "\n", " \n"
=> "Hello \nWorld"
text2 = "Hello\n\nWorld"
text2.sub! "\n\n", " \n"
=> "Hello \n\nWorld"
SHOULD BE: => "Hello\n\nWorld"
You can use the regular expression /(?<!\n)\n(?!\n)/, which matches a \n only when it's not prefixed with another \n and not followed by a \n.
text1 = "Hello\nWorld"
# => "Hello\nWorld"
text1.sub /(?<!\n)\n(?!\n)/, " \n"
# => "Hello \nWorld"
text2 = "Hello\n\nWorld"
# => "Hello\n\nWorld"
text2.sub /(?<!\n)\n(?!\n)/, " \n"
# => "Hello\n\nWorld"
Here's another way:
r = /\n+/
"Hello\nWorld".sub(r) { |s| (s.size==1) ? " \n" : s }
#=> "Hello \nWorld"
"Hello\n\nWorld".sub(r) { |s| (s.size==1) ? " \n" : s }
#=> "Hello\n\nWorld"
and one more:
h = Hash.new { |h,k| h[k] = k }.update("\n"=>" \n")
#=> {"\n"=>" \n"}
"Hello\nWorld".sub(r,h)
#=> "Hello \nWorld"
"Hello\n\nWorld".sub(r,h)
#=> "Hello\n\nWorld"
In the latter method, each string of one or more consecutive newline characters is passed to the hash. If it's a single newline, "\n" is replaced with h["\n"] #=> " \n". If it's two or more newlines, say s = "\n\n", and h does not have a key equal to s (initally it won't), the key-value pair s=>s will be added to h (because of the default value defined for the hash) and s will be replaced by itself.
Another solution you could use:
string = "Hello\nWorld"
string.split("\n") == string.split("\n").reject(&:empty?) ? string.sub!("\n", " \n") : string
#=> "Hello \nWorld"
string = "Hello\n\nWorld"
string.split("\n") == string.split("\n").reject(&:empty?) ? string.sub!("\n", " \n") : string
#=> "Hello\n\nWorld"

Populating a ruby array reading from a file

I'm trying to populate a multi-dimensional array in ruby by reading from a file. Here is the code:
class Maze
attr_accessor :array
def initialize(filename)
handler = File.open(filename,"r")
#array = Array.new(10,Array.new(10))
n = 0;
i = 0;
while(line = handler.gets) do
i = 0
line.chomp.each_char do |char|
p char
#array[n][i] = char
i += 1
end #iterator over character in every line
n += 1
end #iterator over file lines
handler.close
end #constructor
end #class
a = Maze.new("maze.txt")
p a.array
Here is the content of the file:
##########
# #
# #### #
# # # #
# # # ?
# # # #
# # #
# #### #
# #
##########
However this line of code (the last line of code)
p a.array
Will print an array 10 by 10 but full of "#" symbols. No spaces nor "?". Something important to note is that
p char
In the block where I assign the array with values prints the right characters. It prints spaces at the right time and question marks etc... I know it's something very simple but it's bugging me and I think I need a fresh pair of eyes to look over it
Why is it that the array has only "#" symbols. Why aren't all the other characters such as " ", "#", "?" in my array? Is the assigning in my code wrongly coded
I'd write the code differently. It's not quite clear what you're trying to do, so here's two different things I'd do:
class Maze
attr_accessor :array
def initialize(filename)
#array = []
File.foreach(filename) do |line|
#array << line.chomp
end
end
end
a = Maze.new("maze.txt")
p a.array
puts a.array
Which outputs:
["##########", "# #", "# #### #", "# # # #", "# # # ?", "# # # #", "# # #", "# #### #", "# #", "##########"]
##########
# #
# #### #
# # # #
# # # ?
# # # #
# # #
# #### #
# #
##########
Or:
class Maze
attr_accessor :array
def initialize(filename)
#array = []
File.foreach(filename) do |line|
#array << line.chomp.split('')
end
end
end
a = Maze.new("maze.txt")
p a.array
puts a.array.map(&:join)
which outputs:
[["#", "#", "#", "#", "#", "#", "#", "#", "#", "#"], ["#", " ", " ", " ", " ", " ", " ", " ", " ", "#"], ["#", " ", "#", "#", "#", "#", " ", " ", " ", "#"], ["#", " ", "#", " ", " ", "#", " ", " ", " ", "#"], ["#", " ", "#", " ", " ", "#", " ", " ", " ", "?"], ["#", " ", "#", " ", " ", "#", " ", " ", " ", "#"], ["#", " ", "#", " ", " ", " ", " ", " ", " ", "#"], ["#", " ", "#", "#", "#", "#", " ", " ", " ", "#"], ["#", " ", " ", " ", " ", " ", " ", " ", " ", "#"], ["#", "#", "#", "#", "#", "#", "#", "#", "#", "#"]]
##########
# #
# #### #
# # # #
# # # ?
# # # #
# # #
# #### #
# #
##########
The primary problem in your code is you're using array references when you assign to the sub-arrays, which results in them all pointing to the same memory. I think you did that because you're used to another language where you have to predefine your array size. Ruby is more friendly than that, allowing us to append to an array easily. I used to << operator, but other methods exist to do the same thing; << is easier to see and understand.
Instead of opening the file, then using a while loop to iterate over it, it's more idiomatic to use foreach, which accomplishes the same thing and automatically closes the file when the block exits.
Also, instead of iterating over each line's characters, simply split the line using split('') which will return an array of characters. Append that to #array and move on.
The culprit is the way you are initializing the array in this line:
#array = Array.new(10,Array.new(10))
this is creating an array of 10 elements and it is initializing all of them with a pointer to another array of 10 elements. All the 10 elements will be sharing the same array. You can see a simplified example in here:
b = Array.new(3,Array.new(3))
b[0][0] = '00'
puts b # you'll see '00' printed 3 times!
One way to fix your program will be to create the array of rows first, then then initialize each row to a different array:
#array = Array.new(10)
for i in 0..9
# initialize each row to a unique array
#array[i] = Array.new(10)
end
# the rest of your code

Ruby: NoMethodError on call to display method

Greeting, I just started learning Ruby a few weeks with this noob book titled Computer Science Programming Basics in Ruby. I've practiced the exercises in each chapter, haven't had too many error, but I keep getting this error in a class I wrote up for the tic-tac-toe game in the last chapter. Here is the board.rb class file I made:
class Board
BOARD_MAX_INDEX = 2
EMPTY_POS = ' '
def initialize(current_player)
#current_player = current_player
#board = Array.new(BOARD_MAX_INDEX + 1) {
Array.new(BOARD_MAX_INDEX + 1) { EMPTY_POS }
}
end
end
def display
puts "+------+"
for row in 0..BOARD_MAX_INDEX
print "| "
for col in 0..BOARD_MAX_INDEX
s = #board[row][col]
if s == EMPTY_POS
print col + (row * 3) + 1
else
print s
end
print " | "
end
puts "\n+------+"
end
end
The class runs fine, but this is the error message in irb when I attempt to access
the display method call:
irb(main):004:0> require '/home/nick/board.rb'
=> true
irb(main):005:0> puts "Starting tic-tac-toe..."
Starting tic-tac-toe...
=> nil
irb(main):006:0> players = ['X', 'O']
=> ["X", "O"]
irb(main):007:0> current_player = players[rand(2)]
=> "O"
irb(main):008:0> b = Board.new(current_player)
=> #<Board:0x00000001c64868 #current_player="O", #board=[[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]>
irb(main):009:0> b.display()
NoMethodError: private method `display' called for #<Board:0x00000001c64868>
from (irb):9
from /usr/bin/irb:12:in `<main>'
Why am I getting this error, what do I have to do to get it to display the board?
You should put the definition of the display method inside the class:
end # of class
def display
...
end
Should be:
def display
...
end
end # of class
Hope it helps.

Resources