Ruby - inheriting from Array - ruby

I'm pretty new to Ruby.
I need to extend Array and I need my class to represent bidimensional arrays.
I've done this:
class MyExtension < Array
def initialize(n)
super(n, Array.new(n, nil))
self[0][0] = "hello"
end
end
This looks theoretically right to me, but when I do:
p MyExtension.new(2)
I get
[["hello", nil], ["hello", nil]]
instead of the expected:
[["hello", nil], [nil, nil]]
What am I getting wrong?

That is a common mistake a beginner often makes. If you do super(n, Array.new(n, nil)), then Array.new(n, nil) will be evaluated only once, giving that same array (same object id) for each row. Since all the rows would be repetition of the same array, modifying one row by self[0][0] = "hello" would change all the other rows.
Instead, do
class MyExtension < Array
def initialize(n)
super(n){Array.new(n, nil)}
self[0][0] = "hello"
end
end
MyExtension.new(2) # => [["hello", nil], [nil, nil]]

Related

How can I modify positions in a two dimensional array?

If I do the following:
table = Array.new(
3,
Array.new(
3,
nil
)
)
# =>
[
[nil, nil, nil],
[nil, nil, nil],
[nil, nil, nil]
]
Now I would like to modify the value at index 2 in the second array, so I would do:
table[1][2] = 2.343
I would now expect to see:
# =>
[
[nil, nil, nil],
[nil, nil, 2.343],
[nil, nil, nil]
]
However what I'm getting is this:
[
[nil, nil, 2.343],
[nil, nil, 2.343],
[nil, nil, 2.343]
]
What am I not getting here?
PS: Running ruby 2.3
For fix with behavior, try next:
empty_table = Array.new(3) { Array.new(3) }
From array manual:
Note that the second argument populates the array with references to the same object. Therefore, it is only recommended in cases when you need to instantiate arrays with natively immutable objects such as Symbols, numbers, true or false.
you are essentially saying create an array with three elements, and put this element (the new array) in each space. The element you are putting in the first array, is just created once. The only way I know to do what you want is using a for loop to push as many new arrays into the first array as you need. something like this:
table = Array.new(1, Array.new(3, 0))
0..1.each do |i|
table.push(Array.new(3, 0)) #add two more arrays to the first dimension
end

Passing not as a variable

I have two functions, each of which pulls items from an array while asking if a certain value is present or not. How could I create a method that can be flipped according to whether I'm asking if the value is present or not? The (!) below is what I'd like to pass as a variable.
ary.select{ |item| (!)item.my_value.nil? }
Is it possible to pass not to as a variable, somewhat like multiplying something by x where x could be 1 or -1?
I'll assume this is in general terms and you are wondering how to pass something that you can use in the implementation of your function to reverse it. Two strategies:
Ruby metaprogramming. You can use the BasicObject#! and Object#itself:
<
def values_from(array, post_mutator:)
array.select { |item| item.nil?.public_send(post_mutator) }
end
array = [1, nil, :b, nil, nil, 'foo']
values_from(array, post_mutator: :itself) # => [nil, nil, nil]
values_from(array, post_mutator: :!) # => [1, :b, "foo"]
Boolean algebra. Basically, you want to find an operation ., for which:
x . true = x
x . false = -x
All possibilities are:
true, true => true
false, false => true
false, true => false
true, false => false
Which obviously leaves the . to be equality. Therefore:
def values_from(array, switcher)
array.select { |item| item.nil? == switcher }
end
array = [1, nil, :b, nil, nil, 'foo']
values_from(array, true) # => [nil, nil, nil]
values_from(array, false) # => [1, :b, "foo"]
ndn's answer addresses how to do this with Ruby meta-programming, but I was hoping for something a little more elegant, so I ended up addressing this by adding to TrueClass and FalseClass (with a RoR initializer).
class TrueClass
def *(value)
!!value
end
end
class FalseClass
def *(value)
!value
end
end
So I can do the following, which feels a bit more natural.
def select_from_ary(present)
ary.select{ |item| present * item.my_value.nil? }
end
If you have a function called presence? that returns true when you checking the item is present and false when you're checking it's absent then:
ary.select{ |item| !!item.my_value == presence? }
Or as a method where the Boolean is passed in:
def get_value_presence(boolean presence?)
ary.select{ |item| !!item.my_value == presence? }
end
Splitting the truesand falses - that is what partition does:
array = [1, nil, :b, nil, nil, 'foo']
nils, non_nils = array.partition{|item| item.nil?} # or: array.partition(&:nil?)

Convert Array to String and back to Array

I have built a presenter class whose only job is to convert a given array into a string. I am test driving the solution, so I started with "[nil, nil, nil]" but each nil will eventually be replaced with letter. That functionality is being handled by another class.
I am trying now to build an interface whose only job is to convert that string back to an array. So I will need to convert e.g. "[\"a\", \"b\", nil]" back to ["a", "b", nil]. But I am stuck.
For example, I'd like to convert
"[nil, nil, nil]"
to
[nil, nil, nil]
How could I do it?
Just use:
eval(string)
eval("[nil, nil, nil]")
Caution:
This is a very insecure method and you have to use it only if you are completely sure that the string contains a SAFE array..
I am guessing that you are producing the string yourself on one side, for example:
arr = [nil, nil, nil]
str = arr.inspect
#=> "[nil, nil, nil]"
Whereas I would advise you to serialize the array using a format such as JSON, YAML or Ruby's built in Marshalling library.
JSON
require 'json'
arr = [nil, nil, nil]
str = JSON.dump(arr)
#=> "[null,null,null]"
JSON.load(str)
#=> [nil, nil, nil]
YAML
require 'yaml'
arr = [nil, nil, nil]
str = YAML.dump(arr)
#=> "---\n- \n- \n- \n"
YAML.load(str)
#=> [nil, nil, nil]
Marshal
arr = [nil, nil, nil]
str = Marshal.dump(arr)
#=> "\x04\b[\b000"
Marshal.load(str)
#=> [nil, nil, nil]

Replace Empty Cells in Array With Nil in Ruby?

I have a two-dimensional array of a bunch of strings, including some empty strings. I want to replace the empty strings with nil. How do I do this in Ruby?
Sample array:
[['cat','','dog',''],['','','fish','','horse']]
Desired output:
[['cat',nil,'dog',nil],[nil,nil,'fish',nil,'horse']]
[['cat','','dog',''],['','','fish','','horse']].map do |arr|
arr.map { |s| s unless s.empty? }
end
# => [["cat", nil, "dog", nil], [nil, nil, "fish", nil, "horse"]]

Arrays misbehaving

Here's the code:
# a = Array.new(3, Array.new(3))
a = [[nil,nil,nil],[nil,nil,nil]]
a[0][0] = 1
a.each {|line| p line}
With the output:
[1, nil, nil]
[nil, nil, nil]
but using the commented line:
[1, nil, nil]
[1, nil, nil]
[1, nil, nil]
So why is that?
The commented line is assigning three of the same reference to the array, so a change to one array will propagate across the other references to it.
As for the 2 arrays vs 3, that's simply a matter of the first line specifying 3 as its first parameter and only specifying 2 array literals in the second line.
To create the nested arrays without having any shared references:
a = Array.new(3) {Array.new(3)}
When passed a block ({...} or do ... end), Array.new will call the block to obtain the value of each element of the array.

Resources