Forum: Ruby [SOLUTION] Hash to OpenStruct (#81)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
912c61d9da47754de7039f4271334a9f?d=identicon&s=25 MenTaLguY (Guest)
on 2006-06-04 20:14
(Received via mailing list)
I've got three.

ATTEMPT #1

My first attempt was pretty simple:

        require 'ostruct'

        def hashes_to_openstructs( obj )
          return obj unless Hash === obj
          OpenStruct.new( Hash[
            *obj.inject( [] ) { |a, (k, v)| a.push k,
hashes_to_openstructs( v ) }
          ] )
        end

The main idea here was to build the OpenStruct all at once, rather than
resorting to a bunch of Object#send( "#{name}=", value ) calls.  To do
this, however, we end up going from a hash, to a sequence of pairs, to a
flat array of alternating keys and values, back to a hash, and then
finally to an OpenStruct.

ATTEMPT #2:

Then I got bored and decided I'd deal with the case of a self-recursive
hash.  My first attempt used lazy.rb
( http://moonbase.rydia.net/software/lazy.rb ); I simply made the above
hashes_to_openstructs() function lazy (by wrapping its innards in
promise {}), then memoized it using a hash:

        require 'ostruct'
        require 'lazy'

        def hashes_to_openstructs( obj, memo={} )
          return obj unless Hash === obj
          memo[obj.object_id] ||= promise {
            OpenStruct.new( Hash[
              *obj.inject( [] ) { |a, (k, v)|
                a.push k, hashes_to_openstructs( v, memo )
              }
            ] )
          }
        end

ATTEMPT #3:

While that's sort of clever, I couldn't help but think there was a
simpler solution.  It involved building the OpenStruct incrementally,
using the same Object#send calls I'd been hoping to avoid.

        def hashes_to_openstructs( obj, memo={} )
          return obj unless Hash === obj
          os = memo[obj] = OpenStruct.new
          obj.each do |k, v|
            os.send( "#{k}=", memo[v] || hashes_to_openstructs( v, memo
) )
          end
          os
        end

Once again, however, memoization wins.

-mental
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-06-05 18:09
(Received via mailing list)
My solution isn't quite as robust as mental's (particularly, no
support for self-reference), but here it is:

  class Object
    def to_openstruct
      self
    end
  end

  class Array
    def to_openstruct
      map{ |el| el.to_openstruct }
    end
  end

  class Hash
    def to_openstruct
      mapped = {}
      each{ |key,value| mapped[key] = value.to_openstruct }
      OpenStruct.new(mapped)
    end
  end

  module YAML
    def self.load_openstruct(source)
      self.load(source).to_openstruct
    end
  end

Usage is simply:

  require 'to_openstruct'
  p YAML.load_openstruct(File.read("sample.yml")

Jacob Fugal
This topic is locked and can not be replied to.