Multidimestional Array - `initialize': wrong number of arguments (1 for 0) - ruby

I'm still new in Ruby on Rails. Today I'm trying to write some codes which can run the the following:
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.output_image
And I'm having trouble setup the initialize. My codes is as below, can someone help me?
Thanks a lot
class Subary
attr_accessor :num1, :num2, :num3, :num4
def initialize (num1, num2, num3, num4)
self.num1 = num1
self.num2 = num2
self.num3 = num3
self.num4 = num4
end
def output_subary
puts "#{num1}#{num2}#{num3}#{num4}"
end
end
# subary = Subary.new(0,0,0,0)
# puts subary.output_subary
class Image
def initialize
#subarys = []
#subarys << Subary.new(:num1, :num2, :num3, :num4)
#subarys << Subary.new(:num1, :num2, :num3, :num4)
#subarys << Subary.new(:num1, :num2, :num3, :num4)
#subarys << Subary.new(:num1, :num2, :num3, :num4)
end
def output_image
#subarys.each do |list|
list.output_subary
end
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.output_image

`initialize': wrong number of arguments (1 for 0)
This error means that, initialize method does not take any argument (0), but you passed one argument to it. Change the initialize method's definition in your Image class. Then, it should work.
class Subary
attr_accessor :num1, :num2, :num3, :num4
def initialize(sub_array)
self.num1 = sub_array[0]
self.num2 = sub_array[1]
self.num3 = sub_array[2]
self.num4 = sub_array[3]
end
def output_subary
puts "#{num1}#{num2}#{num3}#{num4}"
end
end
# subary = Subary.new(0,0,0,0)
# puts subary.output_subary
class Image
def initialize(array_of_arrays)
#subarys = []
#subarys << Subary.new(array_of_arrays[0])
#subarys << Subary.new(array_of_arrays[1])
#subarys << Subary.new(array_of_arrays[2])
#subarys << Subary.new(array_of_arrays[3])
end
def output_image
#subarys.each do |list|
list.output_subary
end
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
])
image.output_image
# => 0000
# => 0100
# => 0001
# => 0000

Related

Nested if else inside .each iteration

I'm wondering if this makes sense or if the syntax is wrong and basically if this is acceptable. I wanted to nest an if/else condition within my iteration of the array.
def change_numbers(first_array, second_array)
second_array.each do |index|
if first_array[index] == 0
first_array[index] = 1
else
first_array[index] = 0
end
end
end
The array is a simple (binary) array and will only consist of 0s and 1s and I want to use the second array's elements as the indices of the first array that I am going to change.
Example:
first_array = [0, 0, 0, 0, 1, 1, 1, 1, 1]
second_array = [3, 5, 7]
Result:
first_array = [0, 0, 0, 1, 1, 0, 1, 0, 1]
If you don't want to use an if/else you can do:
second_array.each do |index|
first_array[index] = (first_array[index] + 1) % 2
end
def change_numbers(first_array, second_array)
second_array.each { |index| first_array[index] = 1 - first_array[index] }
end
A bit-wise XOR:
ar = [0, 0, 0, 0, 1, 1, 1, 1, 1]
indices = [3, 5, 7]
indices.each{|i| ar[i] ^= 1 }
You can try this -
def change_numbers(first_array, second_array)
second_array.each do |index|
first_array[index] = ((first_array[index] == 0) ? 1 : 0)
end
end

Changing elements in multi-dimensional array

Inside arrays, I have 0s and one 1.
class Image
def initialize(rows)
#rows = rows
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
])
I want the numbers that are located up, down, left, and right to turn 1 as well. I tried to do this by manipulating column_index and row_index. The code is:
class Image
def blur
#rows_copy = Array.new(#rows.size) {Array.new(#rows.first.size)}
#rows.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
blur_location(row_index,column_index)
end
end
#rows = #rows_copy
end
def blur_location (row_index, column_index)
if #rows[row_index][column_index] == 1
#rows_copy[row_index][column_index] = 1
#rows_copy[row_index + 1][column_index] = 1
#rows_copy[row_index - 1][column_index] = 1
#rows_copy[row_index][column_index + 1] = 1
#rows_copy[row_index][column_index - 1] = 1
else
#rows_copy[row_index][column_index] = 0
end
end
def output_image
#rows.each_with_index do |row, row_index|
puts row.join('')
end
end
end
image.blur
image.output_image
But only half of the code is working (i.e., the top and left turns to 1, but not the other two).
the code almost works as expected but you are a victim of the following piece of code:
else
#rows_copy[row_index][column_index] = 0
end
What happens is the when you hit the '1' you set everything as expected, but when you move on and you hit the zeros that are near the '1' (to the right and down as you're processing) you are resetting the rows_copy to zero.
Here is a revised version of the code the does the right thing (notice how the copy is all first set to 0 and after that only 1s are marked):
#!/usr/bin/env ruby
class Image
def initialize(rows)
#rows = rows
end
def blur
#rows_copy = Array.new(#rows.size) {Array.new(#rows.first.size)}
#rows.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
set_zero(row_index,column_index)
end
end
#rows.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
blur_location(row_index,column_index)
end
end
#rows = #rows_copy
end
def set_zero(row_index, column_index)
#rows_copy[row_index][column_index] = 0
end
def blur_location (row_index, column_index)
if #rows[row_index][column_index] == 1
#rows_copy[row_index][column_index] = 1
#rows_copy[row_index + 1][column_index] = 1
#rows_copy[row_index - 1][column_index] = 1
#rows_copy[row_index][column_index + 1] = 1
#rows_copy[row_index][column_index - 1] = 1
end
end
def output_image
#rows.each_with_index do |row, row_index|
puts row.join('')
end
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
])
image.blur
image.output_image

ruby array sum of elements with structure conversion

I have
{
3=>[
{63=>[5, 0, 1, 0]},
{64=>[0, 0, 0, 0]},
{65=>[0, 1, 2, 2]}
],
1=>[
{31=>[2, 0, 0, 0]},
{32=>[0, 0, 3, 0]}
]
}
I need to convert into
{ 3 => [5,1,3,2], 1 => [2,0,3,0] }
h= {
3=>[
{63=>[5, 0, 1, 0]},
{64=>[0, 0, 0, 0]},
{65=>[0, 1, 2, 2]}
],
1=>[
{31=>[2, 0, 0, 0]},
{32=>[0, 0, 3, 0]}
]
}
p h.map{ |k, v| { k=> v.map(&:values).flatten(1).transpose.map{ |r| r.reduce(:+) } } }
# => [{3=>[5, 1, 3, 2]}, {1=>[2, 0, 3, 0]}]
It's nothing difficult, you just need a little attention.
a = {
3=>[
{63=>[5, 0, 1, 0]},
{64=>[0, 0, 0, 0]},
{65=>[0, 1, 2, 2]}
],
1=>[
{31=>[2, 0, 0, 0]},
{32=>[0, 0, 3, 0]}
]
}
b = a.each_with_object({}) do |(k, v), memo|
res = []
v.each do |h|
h.each do |_, v2|
v2.each_with_index do |el, idx|
res[idx] ||= 0
res[idx] += el
end
end
end
memo[k] = res
end
b # => {3=>[5, 1, 3, 2], 1=>[2, 0, 3, 0]}
Here's some readable variable names and a basic explanation.
a = {
3=>[
{63=>[5, 0, 1, 0]},
{64=>[0, 0, 0, 0]},
{65=>[0, 1, 2, 2]}
],
1=>[
{31=>[2, 0, 0, 0]},
{32=>[0, 0, 3, 0]}
]
}
b = a.each_with_object({}) do |(key, sub_hashes), result|
# Get the subarray for each nested hash (Ignore keys on the nested hashes)
# Also flattening while mapping to get appropriate array of arrays
value = sub_hashes.flat_map(&:values).
# Transpose each row into a column
# e.g. [[5,0,1,0], [0,0,0,0], [0,1,2,2]] becomes [[5,0,0], [0,0,1], [1,0,2], [0,0,2]]
transpose.
# Sum each column
# e.g. [1,0,2] = 1 + 0 + 2 = 3
map { |column| column.reduce(0, :+) }
# Update results set (Could also get rid of intermediate variable 'value' if you wish)
result[key] = value
end
puts b # => {3=>[5, 1, 3, 2], 1=>[2, 0, 3, 0]}
puts b == {3 => [5,1,3,2], 1=>[2,0,3,0]}
Edit: Now using flat_map!

Ruby: Refactoring a complicated nested-loop method

I'm trying to get rid of duplication in my code. I have a method that populates a checkerboard with checkers:
def populate_checkers
evens = [0, 2, 4, 6]
odds = [1, 3, 5, 7]
0.upto(2) do |x_coord|
if x_coord.even?
evens.each do |y_coord|
red_checker = Checker.new(x_coord, y_coord, :red)
#board[x_coord][y_coord] = red_checker
end
elsif x_coord.odd?
odds.each do |y_coord|
red_checker = Checker.new(x_coord, y_coord, :red)
#board[x_coord][y_coord] = red_checker
end
end
end
5.upto(7) do |x_coord|
if x_coord.even?
evens.each do |y_coord|
black_checker = Checker.new(x_coord, y_coord, :black)
#board[x_coord][y_coord] = black_checker
end
elsif x_coord.odd?
odds.each do |y_coord|
black_checker = Checker.new(x_coord, y_coord, :black)
#board[x_coord][y_coord] = black_checker
end
end
end
end
How can I remove duplication and still get the precise behavior I need?
You can try to extract a method and then extract a block into lambda. Then your code will be readable and loose of duplication
def populate_checkers
0.upto(2) do |x_coord|
populate_checker(x_coord, :red)
end
5.upto(7) do |x_coord|
populate_checker(x_cord, :black)
end
end
def populate_checker(x_coord, color)
evens = [0, 2, 4, 6]
odds = [1, 3, 5, 7]
apply_checker = lambda do |y_coord|
checker = Checker.new(x_coord, y_coord, color)
#board[x_coord][y_coord] = checker
end
if x_coord.even?
evens.each(&apply_checker)
elsif x_coord.odd?
odds.each(&apply_checker)
end
end
def populate_checkers
evens = [0, 2, 4, 6]
odds = [1, 3, 5, 7]
[0.upto(2), 5.upto(7)].each_with_index do |enum, i|
enum.each do |x_coord|
(x_coord.even? ? evens : odds).each do |y_coord|
checker = Checker.new(x_coord, y_coord, i == 0 ? :red : :black)
#board[x_coord][y_coord] = checker
end
end
end
end
There's probably a better way to do the counting bit, but that's what I got.
Here's a possibly better solution...
def populate_checkers
{ :red => (0..2), :black => (5..7) }.each do |color, range|
range.each do |x_coord|
(x_coord.even? ? 0 : 1).step(7, 2) do |y_coord|
checker = Checker.new(x_coord, y_coord, color)
#board[x_coord][y_coord] = checker
end
end
end
end
0.upto(2) do |x|
0.upto(7) do |y|
#board[x][y]=Checker.new(x, y, :red) if (x+y).even?
end
end
This is only for the reds.

Can I create an array in Ruby with default values?

Perl is pretty nice about default values:
: jmglov#laurana; perl -e '#foo; printf "%d\n", $foo[123]'
0
: jmglov#laurana; perl -e '%foo; printf "%d\n", $foo{bar}'
0
Ruby can do the same, at least for hashes:
>> foo = Hash.new(0)
=> {}
>> foo[:bar]
=> 0
But the same seemingly does not work for arrays:
>> foo = Array.new(0)
=> []
>> foo[123]
=> nil
>> foo[124] = 0
=> 0
>> foo[456] = 0
=> 0
>> foo[455,456]
=> [nil, 0]
Is it possible to supply a default value for arrays, so when they are auto-extended, they're filled with 0 instead of nil?
Of course I can work around this, but at a cost to expressiveness:
>> foo[457,458] = 890, 321
=> [890, 321]
>> foo[456] += 789
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.+
>> foo.inject(0) {|sum, i| sum += (i || 0) }
=> 1211
>> foo.inject(:+)
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.+
Update 1: One of my colleagues pointed out that I can use #compact to solve the #inject issue, and #to_i to solve the standard element-at-index issue:
>> foo.include? nil
=> true
>> foo.compact.inject(:+)
=> 1211
>> foo[456,457]
=> [0, 890, 321]
>> foo[455..457]
=> [nil, 0, 890]
>> foo[455..457].map(&:to_i)
=> [0, 0, 890]
Update 2: Thanks to Andrew Grimm for a solution to the += issue:
>> foo = []
=> []
>> def foo.[](i)
>> fetch(i) {0}
>> end
=> nil
>> foo[4]
=> 0
>> foo
=> []
>> foo[4] += 123
=> 123
>> foo
=> [nil, nil, nil, nil, 123]
Update 3: this is starting to look like whack-a-mole!
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer
But we can deal with that:
>> def foo.[](index)
>> if index.is_a? Range
>> index.map {|i| self[i] }
>> else
?> fetch(index) { 0 } # default to 0 if no element at index; will not cause auto-extension of array
>> end
>> end
=> nil
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
=> [nil, 123]
I now have to admit (sheepishly) that I'll subclass Array to avoid cluttering my code:
class MyClass
class ArrayWithDefault < Array
def [](index)
if index.is_a? Range
index.map {|i| self[i] }
else
fetch(index) { 0 } # default to 0 if no element at index; will not cause auto-extension of array
end
end
end
end
Thanks for all the creative solutions. TIMTOWTDI indeed!
Not auto extended, but initialized to the specified length with a default value:
>> Array.new(123, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Given that Ruby returns nil for a non-existing element (as opposed to index-out-of-bounds type error), you could just use an "or":
a = [1,2,3]
puts a[5] # => nil
puts a[5] || "a default" # => a default
You could take the monkey patch approach, but you probably would not want to do this in anything larger than a 1-file script:
a = [1,2,3]
def a.[](index)
self.at(index) || "a default"
end
puts a[5] # => "a default"
The easiest way would be:
new_array = Array.new(size, default_value)
For example:
new_array = Array.new(5,"foo")
Another approach would be overriding the Array#[] method and return the default value if there is no item
class Array
def [](index)
self.at(index) ? self.at(index) : 0
end
end
and
arr = [1,2,3]
puts arr[0] # print 1
puts arr[5] # print 0
I'll put Johans elegant solution out there: foo.compact.inject(:+)
If you're dealing with integers you can call to_i:
foo = []
foo[100]
#=> nil
foo[100].to_i
#=> 0
foo[100] = 3
foo[100]
#=> 3
UPD
Oh, I didn't read all topic :)
so you can use this:
foo.inject{|a,b| a.to_i + b.to_i }
which, actually, not the smartest one
I think an array is the wrong abstraction if you want to auto extend the array. Add another level of abstraction.
Edit (from our discussion): The important thing is that the code to achieve your goal is located in the right place (single responsibility principle), and that place is not your "client code", hence the need for a new class. Extending the existing Array class (through inheritance/mixin) is probably better than encapsulating the wanted behaviour in an entierly new class.

Resources