I have a class like this:
class Tiles
attr_accessor :board
def initialize
#board = Array.new(4) { Array.new(4) { 0 } }
end
...
Later, I call this method:
def display_board
padded_board = #board.clone
padded_board.each_with_index do |row, x|
row.each_with_index do |item, y|
padded_board[x][y] = pad_number(item)
end
puts row.join ' '
end
end
Anytime I change padded_board, #board gets changed as well. I can't figure out why this is happening. Any ideas?
That is because when you clone, you are creating a new instance of an array #board, but its elements, which are also arrays, are not being replaced with new instances. You are not going deeply enough to clone the end elements. You need to do deep clone. You might want to look at some answers and gems suggested here or here.
The reason is that array is in fact a set of pointers to other objects, in your case those objects are another arrays. When you are cloning array, you are creating new pointers, which points to the same objects. By calling padded_board[x][y]= you are modifying the padded_board[x] array, which is referenced by both #board and padded_board.
To fix it, you need to duplicate not only the array, but also each element of this array. Int his case this should be sufficient:
padded_board = #board.map(&:clone)
In more general case it is useful to extend Array with deep_dup method. Rails have this method defined within ActiveSupport module, whcih can also be used in any non-rails project by adding `require 'active_support/core_ext/object/deep_dup'.
Related
I am working on building the flatten method in Ruby. Here is my code:
require 'byebug'
class Array
##new_array = []
def new_flatten
self.each do |element|
if !element.is_a? Array
##new_array << element
elsif element.is_a? Array
element.new_flatten
end
end
##new_array
end
end
test_array = ([1,2,3,[4,5,6,[7]]])
puts test_array.new_flatten == test_array.flatten
My question is this. Do I need to use the class level variable? I would like to use an instance variable but it doesn't seem to get called when I do:
test_array = []
or
Array.new([])
So I had to create a class-level variable to hold the new flattened array.
The problem with using a class variable is that the same one is visible to all the instances, and new_flatten is an operation for an instance. You should refactor your new_flatten algorithm so it doesn't rely on what is, in effect, a "global" variable for the method calls.
For example, you can use a local variable and Ruby's facility to append arrays with +:
class Array
def new_flatten
a = []
self.each do |element|
if element.is_a? Array
a += element.new_flatten
else
a << element
end
end
a
end
end
Also, as some tweaks of style, when doing an if-else, it's often clearer to state the positive logic first. And, you don't need the elsif in this case since the else is the complement of the if. So rather than:
if not something
do_stuff
elsif opposite_of_not_something
do_other_stuff
end
It's clearer to say:
if something
do_stuff
else
do_other_stuff
end
Your question is really confusing. In your title, you talk about calling initialize, but there is no initialize in your code. In your question, you talk about calling an instance variable, but you can't call variables, only methods.
However, implementing flatten is just a simple fold, I don't see the need for storing any intermediate state:
class Array
def new_flatten
inject([]) {|acc, el| acc + Array(case el when Array then el.new_flatten else el end) }
end
end
I have a multi-level tree structure and i'm trying to return an array of ancestors for an object which can have 1-3 ancestors. I have a working method, however it's complex and I would prefer to use a loop, anyone know how I can using ruby?
def ancestors
#a = []
#a.push(parent) if parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
return #a
end
Assuming I understand your classes right.. I was thinking something like this
def ancestors
(parent.present? ? [parent, parent.ancestors] :[]).flatten
end
If Parent is present, it returns an array consisting of parent and its ancestors. The flatten is required because each level adds a array layer.
Off topic. return is considered bad style in ruby, and unless you need it, there is no need for this list to be a member variable.
This is a job for recursion.
You need to make a function which calls itself.
Something like this....
def ancestors
if self.parent.present?
ancestors << self.parent.ancestors
else
return self
end
end
It is fairly simple to do this with an iteration as well, you could try
def ancestors
#a = []
anc=parent
while anc.present? do
#a.push anc
anc=anc.parent
end
return #a
end
(not tried as I do not have your data structure)
class A
attr_accessor :dab
....
end
Now I have an array of instances of A, say
arr = [A.new, A.new, A.new]
And now I want to set a value to all instances of class A present in the array arr. Is there a shortcut in ruby/rails to do this?
In addition, I do have A inherited from ActiveRecord::Base
And my actual need is:
A.find_all_by_some_condition.all.dabs = 2
So, all found objects will have dab set to 2.
Is there shortcut for this?
To get the items of class A from an array you can use select/find_all
arr.select { |el| el.class == A } or arr.select { |el| A === el }
To achieve your actual result though you are looking to assign a value to several objects, not their corresponding class. class A does not define the actual objects it just defines the blueprint that the objects use when getting created. So finding a way to assign a value of all instances of A is not what you are after (although I might have missed the point of what you were asking for)
To assign a value to an array of object this works:
A.find_all_by_some_condition.each { |a| a.dab = 2 }
Perhaps you want to save them after that, now arr.each(&:save) might come in handy. Go look up the ampersand if you don't know it already. Very useful.
You can't do that directly by default, however you could build something like that using Ruby's method_missing.
Two solutions:
Solution 1 - Use a wrapper class
We'll call this class MArray for multi-assign-array.
class MArray
def initialize(inner_array)
#inner = inner_array
end
def method_missing(meth, value)
# Check if assignement, and if it is then run mass-assign
if meth.to_s =~ /^\w+=$/
#inner.each { |itm| itm.send(meth, value) }
else
raise ArgumentError, "MArray: not an assignment"
end
end
end
We also need to add support for MArray in Array, so that the wrapping will take place. We'll call the method mas for "mass-assignment":
class Array
def mas
# Wrap MArray around self
MArray.new(self)
end
end
Usage is simple:
Blob = Struct.new(:dab)
arr = [Blob.new] * 3
arr.mas.dab = 123
arr
=> [#<struct Blob dab=123>, #<struct Blob dab=123>, #<struct Blob dab=123>]
Solution 2 - Create mass-assignment support directly into Array
This is a bit more "dangerous" since we directly modify method_missing in Array. It could create some strange side-effects (for example if method_missing has already been redefined by some other library, or you accidentally call a mass-assign while you didn't mean to).
It works by trying to detect assignments with plural words (words ending with s), and then triggering the mass-assignment:
class Array
def method_missing(meth, *args, &block)
# Check for plural assignment, and as an added safety check also
# see if all elements in the array support the assignment:
if meth.to_s =~ /^(\w+)s=$/ &&
self.all? { |itm| itm.respond_to?("#{$1}=") }
self.each { |itm| itm.send("#{$1}=", *args) }
else
super
end
end
end
Usage then becomes even shorter than with MArray:
Blob = Struct.new(:dab)
arr = [Blob.new] * 3
arr.dabs = 123
arr
=> [#<struct Blob dab=123>, #<struct Blob dab=123>, #<struct Blob dab=123>]
I'd like to add something like a callback function to a Ruby array, so that when elements are added to that array, this function is called.
One thing I can think of is to override all methods (like <<, =, insert, ...) and call that callback from there.
Is there an easier solution?
The following code only invokes the size_changed hook when the array size has changed and it is passed the new size of the array:
a = []
class << a
Array.instance_methods(false).each do |meth|
old = instance_method(meth)
define_method(meth) do |*args, &block|
old_size = size
old.bind(self).call(*args, &block)
size_changed(size) if old_size != size
end if meth != :size
end
end
def a.size_changed(a)
puts "size change to: #{a}"
end
a.push(:a) #=> size change to 1
a.push(:b) #=> size change to 2
a.length
a.sort!
a.delete(:a) #=> size change to 1
You should probably create your own class that wraps array. You don't want to override a core class with a callback like you are describing, not only does that make the code brittle but it becomes less expressive for future developers who may not be expecting Array to make a callback.
Use the "Observer" pattern to be notified of changes in the size of the array you wish to observer: Ruby Observer This saves you from having to override all methods that add an element to the array
I have an object Results that contains an array of result objects along with some cached statistics about the objects in the array. I'd like the Results object to be able to behave like an array. My first cut at this was to add methods like this
def <<(val)
#result_array << val
end
This feels very c-like and I know Ruby has better way.
I'd also like to be able to do this
Results.each do |result|
result.do_stuff
end
but am not sure what the each method is really doing under the hood.
Currently I simply return the underlying array via a method and call each on it which doesn't seem like the most-elegant solution.
Any help would be appreciated.
For the general case of implementing array-like methods, yes, you have to implement them yourself. Vava's answer shows one example of this. In the case you gave, though, what you really want to do is delegate the task of handling each (and maybe some other methods) to the contained array, and that can be automated.
require 'forwardable'
class Results
include Enumerable
extend Forwardable
def_delegators :#result_array, :each, :<<
end
This class will get all of Array's Enumerable behavior as well as the Array << operator and it will all go through the inner array.
Note, that when you switch your code from Array inheritance to this trick, your << methods would start to return not the object intself, like real Array's << did -- this can cost you declaring another variable everytime you use <<.
each just goes through array and call given block with each element, that is simple. Since inside the class you are using array as well, you can just redirect your each method to one from array, that is fast and easy to read/maintain.
class Result
include Enumerable
def initialize
#results_array = []
end
def <<(val)
#results_array << val
end
def each(&block)
#results_array.each(&block)
end
end
r = Result.new
r << 1
r << 2
r.each { |v|
p v
}
#print:
# 1
# 2
Note that I have mixed in Enumerable. That will give you a bunch of array methods like all?, map, etc. for free.
BTW with Ruby you can forget about inheritance. You don't need interface inheritance because duck-typing doesn't really care about actual type, and you don't need code inheritance because mixins are just better for that sort of things.
Your << method is perfectly fine and very Ruby like.
To make a class act like an array, without actually inheriting directly from Array, you can mix-in the Enumerable module and add a few methods.
Here's an example (including Chuck's excellent suggestion to use Forwardable):
# You have to require forwardable to use it
require "forwardable"
class MyArray
include Enumerable
extend Forwardable
def initialize
#values = []
end
# Map some of the common array methods to our internal array
def_delegators :#values, :<<, :[], :[]=, :last
# I want a custom method "add" available for adding values to our internal array
def_delegator :#values, :<<, :add
# You don't need to specify the block variable, yield knows to use a block if passed one
def each
# "each" is the base method called by all the iterators so you only have to define it
#values.each do |value|
# change or manipulate the values in your value array inside this block
yield value
end
end
end
m = MyArray.new
m << "fudge"
m << "icecream"
m.add("cake")
# Notice I didn't create an each_with_index method but since
# I included Enumerable it knows how and uses the proper data.
m.each_with_index{|value, index| puts "m[#{index}] = #{value}"}
puts "What about some nice cabbage?"
m[0] = "cabbage"
puts "m[0] = #{m[0]}"
puts "No! I meant in addition to fudge"
m[0] = "fudge"
m << "cabbage"
puts "m.first = #{m.first}"
puts "m.last = #{m.last}"
Which outputs:
m[0] = fudge
m[1] = icecream
m[2] = cake
What about some nice cabbage?
m[0] = cabbage
No! I meant in addition to fudge
m.first = fudge
m.last = cabbage
This feels very c-like and I know Ruby
has better way.
If you want an object to 'feel' like an array, than overriding << is a good idea and very 'Ruby'-ish.
but am not sure what the each method
is really doing under the hood.
The each method for Array just loops through all the elements (using a for loop, I think). If you want to add your own each method (which is also very 'Ruby'-ish), you could do something like this:
def each
0.upto(#result_array.length - 1) do |x|
yield #result_array[x]
end
end
If you create a class Results that inherit from Array, you will inherit all the functionality.
You can then supplement the methods that need change by redefining them, and you can call super for the old functionality.
For example:
class Results < Array
# Additional functionality
def best
find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
delete(ininteresting_result)
super
end
end
Alternatively, you can use the builtin library forwardable. This is particularly useful if you can't inherit from Array because you need to inherit from another class:
require 'forwardable'
class Results
extend Forwardable
def_delegator :#result_array, :<<, :each, :concat # etc...
def best
#result_array.find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
#result_array.delete(ininteresting_result)
#result_array.compact
self
end
end
In both of these forms, you can use it as you want:
r = Results.new
r << some_result
r.each do |result|
# ...
end
r.compact
puts "Best result: #{r.best}"
Not sure I'm adding anything new, but decided to show a very short code that I wish I could have found in the answers to quickly show available options. Here it is without the enumerator that #shelvacu talks about.
class Test
def initialize
#data = [1,2,3,4,5,6,7,8,9,0,11,12,12,13,14,15,16,172,28,38]
end
# approach 1
def each_y
#data.each{ |x| yield(x) }
end
#approach 2
def each_b(&block)
#data.each(&block)
end
end
Lets check performance:
require 'benchmark'
test = Test.new
n=1000*1000*100
Benchmark.bm do |b|
b.report { 1000000.times{ test.each_y{|x| #foo=x} } }
b.report { 1000000.times{ test.each_b{|x| #foo=x} } }
end
Here's the result:
user system total real
1.660000 0.000000 1.660000 ( 1.669462)
1.830000 0.000000 1.830000 ( 1.831754)
This means yield is marginally faster than &block what we already know btw.
UPDATE: This is IMO the best way to create an each method which also takes care of returning an enumerator
class Test
def each
if block_given?
#data.each{|x| yield(x)}
else
return #data.each
end
end
end
If you really do want to make your own #each method, and assuming you don't want to forward, you should return an Enumerator if no block is given
class MyArrayLikeClass
include Enumerable
def each(&block)
return enum_for(__method__) if block.nil?
#arr.each do |ob|
block.call(ob)
end
end
end
This will return an Enumerable object if no block is given, allowing Enumerable method chaining