Parent-child relationship
I don't think you should worry about calling new like that, but you can avoid it by embracing objects and Ruby, it's not perl, you're not stuck with hashes! Make your data into objects and if you need a hash representation of it then write a method that converts the object into a hash.

If you want to be able to keep track of objects that's when I'd use a hash, by putting it into a class variable, or even better, a class instance variable.



Additional:

Since you've mentioned JSON in the comments, or perhaps you'd be passed the hash, then I'd still use objects, just add a way to coerce a hash into an object. A library I've forked called Grackle communicates with Twitter and can receive either JSON or XML. It does this by loading handlers, which means the same data object class can receive many differing types of input and still produce an object with a standard interface.

module Handlers
  module JSONHandler
    require 'json'

    def from_json json
      hash = JSON.parse(json)
      recursive hash
    end

    # You might want to add an extra check to stop an infinite loop
    # Beware the data!
    def recursive hash
      return nil if hash.nil?
      return hash unless hash.respond_to? :fetch
      Item.new id: hash["id"], parent: recursive(hash["parent"])
    end
  end
end


class Item
  extend Handlers::JSONHandler

  def self.items  
    @items ||= {}    
  end    


  def initialize(id:, parent:nil)  
    @parent = parent    
    @id = id    
    self.class.items[@id] = self  
  end    

  attr_reader :parent, :id  

  def to_h  
    h = {id: @id, parent: (@parent &amp;&amp; parent.id) }    
    h.reject{|k,v| v.nil? }    
  end    

  alias_method :to_hash,:to_h  
end
# =&gt; Item
parent = Item.new id: 3
# =&gt; #&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;
child = Item.new id: 7, parent: parent
# =&gt; #&lt;Item:0x007fa94a9abe38 @parent=#&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;, @id=7&gt;
Item.items
# =&gt; {3=&gt;#&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;, 7=&gt;#&lt;Item:0x007fa94a9abe38 @parent=#&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;, @id=7&gt;}
Item.items[3]
# =&gt; #&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;
Item.items[7]
# =&gt; #&lt;Item:0x007fa94a9abe38 @parent=#&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;, @id=7&gt;
Item.items[7].parent
# =&gt; #&lt;Item:0x007fa94a4010c8 @parent=nil, @id=3&gt;
parent.to_h
# =&gt; {:id=&gt;3}
child.to_hash
# =&gt; {:id=&gt;7, :parent=&gt;3}
json_parent = parent.to_h.to_json
# =&gt; "{\"id\":3}"
json_child = child.to_h.to_json
# =&gt; "{\"id\":7,\"parent\":3}"
parent2 = Item.from_json json_parent
# =&gt; #&lt;Item:0x007fb543e26940 @parent=nil, @id=3&gt;
child2 = Item.from_json json_child
# =&gt; #&lt;Item:0x007fb5431d7270 @parent=3, @id=7&gt;


and to make calling it easy:

def self.Item(*args, **keywords)
  case args.first
  when String # it's JSON
    Item.from_json args.first
  when Hash
    # you might want to nick the recursive bit from the handler
    # or write an Hash handler etc
    Item.new id: args.first["id"], parent: args.first["parent"]
  when Item
    args.first
  else
    Item.new id: keywords[:id], parent: keywords[:parent]
  end
end

Item(parent)
# =&gt; #&lt;Item:0x007fb9b2765160 @parent=nil, @id=3&gt;
Item(child)
# =&gt; #&lt;Item:0x007fb9b272f330 @parent=3, @id=7&gt;
Item(json_parent)
# =&gt; #&lt;Item:0x007fb9b26fdd30 @parent=nil, @id=3&gt;
Item(json_child)
# =&gt; #&lt;Item:0x007fb9b26c7dc0 @parent=3, @id=7&gt;
Item(parent.to_h)
# =&gt; #&lt;Item:0x007fb9b26965b8 @parent=nil, @id=3&gt;
Item(child.to_h)
# =&gt; #&lt;Item:0x007fb9b2664928 @parent=3, @id=7&gt;
Item(id: 3)
# =&gt; #&lt;Item:0x007fb9b262ed50 @parent=nil, @id=3&gt;
Item(id: 7, parent: parent)
# =&gt; #&lt;Item:0x007fb9b25fc990 @parent=#&lt;Item:0x007fb9b23bd7b0 @parent=nil, @id=3&gt;, @id=7&gt;