luca
luca

Reputation: 51

Append Hash to Array

I have a little problem with this exercise, can you help me? I tried to edit a script, but for each time that I create new instance the last element overwrites the Others (i.e. )

The class must provide a functionality for the combination of consequent blocks: i.e. it must be able to inherit from the previous block the instructions that have not been explicitly defined for the current block.

blocks = [] 

blocks << Block.new({ X:0, Y:100, Z:20}) # p blocks:({ X:0, Y:100, Z:20}) 

blocks << Block.new({X:100}, blocks[-1]) #p blocks:({ X:100, Y:100, Z:20},{ X:100, Y:100, Z:20})

blocks << Block.new({Y:0}, blocks[-1]) #p blocks:({ X:100, Y:0, Z:20},{ X:100, Y:0, Z:20},{ X:100, Y:0, Z:20})

My code with problems is:

#!/usr/bin/env ruby

    class Block


      $instance_2 = true
      $instance_3 = true


      attr_reader :out


      def initialize(out, previous = nil )

        @out = update(out, previous)
      end # end initialize


      def update(actual, previous)
        puts "IN UPDATE: box is: #{previous}"
        box = previous
        if box != nil      
          puts "IF: changing is: #{actual}"
          actual.each do |key, value|
            box[key] = value               
          end  
           box  
        else
          puts "ELSE"
             actual   
        end # end if 
      end # end update

      def inspect
        @out
      end



    end # end Block

    blocks = []
    puts "\n#----------------------1 INSTANCE----------------------#" 
    blocks << Block.new({G: "00", X: 100, Y:100, Z: 100})
    puts "\n#----------------------blocks element----------------------#" 
    p blocks
    puts "last block is: #{blocks[-1].out}"

    puts "\n#----------------------2 INSTANCE----------------------#"  if $instance_2
    blocks << Block.new({G: "01"}, blocks[-1].out) if $instance_2 
    puts "\n#----------------------blocks element----------------------#"if $instance_2 
    p blocks if $instance_2
    puts "last block is: #{blocks[-1].out}"


    puts "\n#----------------------3 INSTANCE----------------------#" if $instance_3
    blocks << Block.new({G: "02"}, blocks[-1].out) if $instance_3 
    puts "\n#----------------------blocks element----------------------#" if $instance_3
    p blocks if $instance_3
    puts "last block is: #{blocks[-1].out}"

    puts "\n#----------------------4 INSTANCE----------------------#" if $instance_3
    blocks << Block.new({G: "03"}, blocks[-1].out) if $instance_3 
    puts "\n#----------------------blocks element----------------------#" if $instance_3
    p blocks if $instance_3
    puts "last block is: #{blocks[-1].out}"

Upvotes: 3

Views: 170

Answers (1)

Matteo Ragni
Matteo Ragni

Reputation: 2956

The question is not exactly clear, but if I understand it correctly it is possible to provide a class that may or not accept a previous Block. What do you think of something like that?

#!/usr/bin/env ruby

class Block < Hash

  def initialize(h, b=nil)
    [:x, :y, :z].each do |s|
      # We start by trying to assign the coordinate that is in the 
      # input hash
      if h[s]
        self[s] = h[s]
      else
      # If the coordinate is not in h, we check for it in b, but we have to
      # remember that if the block that we are providing in b does not have
      # the method :[] or the coordinate is nil we may raise an exception
        begin
          self[s] = b[s]
          raise if not self[s]
        rescue
          raise(Exception, "Cannot initialize block.")   
        end  
      end
    end 
  end

  def inspect
    "block:(x: #{self[:x]}, y: #{self[:y]}, z: #{self[:z]}"
  end
end

# Let's try it!
blocks = [] 
blocks << Block.new({ x:0, y:100, z:20}) 
puts blocks
puts
blocks << Block.new({x:100}, blocks[-1]) 
puts blocks
puts
blocks << Block.new({y:0}, blocks[-1])
puts blocks
puts

With respect to your code

Let's consider only the update method:

def update(actual, previous)
    puts "IN UPDATE: box is: #{previous}"
    box = previous                 # ! ! ! ! ! ! ! ! WHOOPS!
    if box != nil      
      puts "IF: changing is: #{actual}"
      actual.each do |key, value|
        box[key] = value               
      end  
       box  
    else
      puts "ELSE"
         actual   
    end # end if 
  end # end update

the "whoops" line is what is creating you the issue. With that line you are giving the reference to previous (your object variable actually contains a reference) to the box variable. What you are actually doing, when you perform some action on box, is modifying what is pointed by both variable.

You can test what I'm saying immediately. Try to modify that line in this way:

box = previous.clone if previous

(nil does not have a method #clone that the rationale behind the if). If you run it again you will get a list of blocks that have not being modified. This is not an efficient code tough, and you should really rethink the update method code.

Upvotes: 2

Related Questions