Let's say I have two classes:
class One
attr_reader :array
def initialize(array)
@array = array
end
end
class Two < One
attr_reader :array
def initialize
@array = []
end
end
I now instantiate one object of class "One" and two objects from class "Two".
array = [1,2]
a = One.new(array)
b = Two.new
c = Two.new
Is it possible to break apart the @array array stored in "a" and place the two elements in "b" and "c" such that:
if element is odd, place it in b.array.
if element is even, place it in c.array.
Output:
b.array = [1]
c.array = [2]
I know this might be legal Ruby as I'm learning how inheritance works...
You can use Enumerable#partition
:
array = [1, 2, 3, 4]
array.partition { |x| x % 2 == 1 }
# => [[1, 3], [2, 4]]
odd, even = array.partition {|x| x % 2 == 1}
odd
# => [1, 3]
even
# => [2, 4]
class One
attr_reader :array
def initialize(array)
@array = array
end
end
class Two < One
attr_reader :array
def initialize
@array = []
end
end
array = [1,2]
a = One.new(array)
b = Two.new
c = Two.new
odd, even = array.partition {|x| x % 2 == 1}
b.array.concat odd
c.array.concat even
b.array
# => [1]
c.array
# => [2]
NOTE: As meagar commented, the problem is not related with inheritance. You don't need to use class here at all.
TL;DR
You don't actually need multiple objects here. In fact, you don't even really need a custom class. If you do use a class, one typical Ruby idiom for this use case would be to pass a block to your getter method. Alternatively, you can define your own custom getter to take an argument.
All Methods Take a Block
In Ruby, all methods implicitly take a block as the last argument. Consider the following example:
class One
attr_reader :array
def initialize array
@array = array
end
end
a = One.new [*1..5]
a.array
#=> [1, 2, 3, 4, 5]
a.array.select { |e| e.odd? }
#=> [1, 3, 5]
a.array.select { |e| e.even? }
#=> [2, 4]
You can get the behavior you want (e.g. different views of your array data) with only a single class, but still have access to the whole data set in stored in @array if you need it.
Arguments to Getters
If you want to store the behavior in the class and provide an interface, one way to do this is to provide an explicit getter method yourself rather than letting Ruby make one for you with #attr_reader. For example, by providing an optional argument to One#array, you can control whether you get the whole array or a subset. Consider the following:
class One
def initialize array
@array = array
end
def array mod=nil
mod ? @array.select { |e| e.modulo(mod).zero? } : @array
end
end
a = One.new [*1..10]
a.array
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a.array 2
#=> [2, 4, 6, 8, 10]
a.array 5
#=> [5, 10]
As long as your objects have a well-defined interface, both options (as well as others) are all equally valid. It really just depends on what you're trying to express with your code, and how flexible your code needs to be.