Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.ruby > #3631 > unrolled thread

Initialize Struct from Hash

Started byBrian Candler <b.candler@pobox.com>
First post2011-04-28 12:00 -0500
Last post2011-04-29 03:22 -0500
Articles 10 — 5 participants

Back to article view | Back to comp.lang.ruby


Contents

  Initialize Struct from Hash Brian Candler <b.candler@pobox.com> - 2011-04-28 12:00 -0500
    Re: Initialize Struct from Hash Jesús Gabriel y Galán <jgabrielygalan@gmail.com> - 2011-04-28 12:05 -0500
      Re: Initialize Struct from Hash Jesús Gabriel y Galán <jgabrielygalan@gmail.com> - 2011-04-28 12:10 -0500
      Re: Initialize Struct from Hash 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-28 12:16 -0500
        Re: Initialize Struct from Hash Jesús Gabriel y Galán <jgabrielygalan@gmail.com> - 2011-04-28 12:19 -0500
    Re: Initialize Struct from Hash Brian Candler <b.candler@pobox.com> - 2011-04-28 15:24 -0500
      Re: Initialize Struct from Hash Joel VanderWerf <joelvanderwerf@gmail.com> - 2011-04-28 15:50 -0500
        Re: Initialize Struct from Hash Robert Klemme <shortcutter@googlemail.com> - 2011-04-29 03:06 -0500
          Re: Initialize Struct from Hash Joel VanderWerf <joelvanderwerf@gmail.com> - 2011-04-29 10:27 -0500
        Re: Initialize Struct from Hash Brian Candler <b.candler@pobox.com> - 2011-04-29 03:22 -0500

#3631 — Initialize Struct from Hash

FromBrian Candler <b.candler@pobox.com>
Date2011-04-28 12:00 -0500
SubjectInitialize Struct from Hash
Message-ID<919600ef40c16b10cb0f01a757234376@ruby-forum.com>
I just want to check I've not missed something here. Is there a built-in
way to initialize a Struct from a hash of key/value pairs?

That is, can I shorten the following?

K1 = Struct.new :foo, :bar

module FixStruct
  def set(h)
    h.each { |k,v| self[k] = v }
    self
  end
end

class K1
  include FixStruct
end

k1 = K1.new.set(:bar=>456, :foo=>123)
p k1

(I'm talking about real Struct here, not OpenStruct etc)

Thanks,

Brian.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [next] | [standalone]


#3632

FromJesús Gabriel y Galán <jgabrielygalan@gmail.com>
Date2011-04-28 12:05 -0500
Message-ID<BANLkTinnJvTxPXORtnne78=eZAmka5-qHQ@mail.gmail.com>
In reply to#3631
On Thu, Apr 28, 2011 at 7:00 PM, Brian Candler <b.candler@pobox.com> wrote:
> I just want to check I've not missed something here. Is there a built-in
> way to initialize a Struct from a hash of key/value pairs?
>
> That is, can I shorten the following?
>
> K1 = Struct.new :foo, :bar
>
> module FixStruct
>  def set(h)
>    h.each { |k,v| self[k] = v }
>    self
>  end
> end
>
> class K1
>  include FixStruct
> end
>
> k1 = K1.new.set(:bar=>456, :foo=>123)
> p k1
>
> (I'm talking about real Struct here, not OpenStruct etc)

ruby-1.8.7-p334 :001 > K = Struct.new :id,:timestamp
 => K
ruby-1.8.7-p334 :002 > K[3,8]
 => #<struct K id=3, timestamp=8>

Jesus.

[toc] | [prev] | [next] | [standalone]


#3634

FromJesús Gabriel y Galán <jgabrielygalan@gmail.com>
Date2011-04-28 12:10 -0500
Message-ID<BANLkTim3zKr-02ZLmp6Dy1yuNDab0O9vYQ@mail.gmail.com>
In reply to#3632
2011/4/28 Jesús Gabriel y Galán <jgabrielygalan@gmail.com>:
> On Thu, Apr 28, 2011 at 7:00 PM, Brian Candler <b.candler@pobox.com> wrote:
>> I just want to check I've not missed something here. Is there a built-in
>> way to initialize a Struct from a hash of key/value pairs?
>>
>> That is, can I shorten the following?
>>
>> K1 = Struct.new :foo, :bar
>>
>> module FixStruct
>>  def set(h)
>>    h.each { |k,v| self[k] = v }
>>    self
>>  end
>> end
>>
>> class K1
>>  include FixStruct
>> end
>>
>> k1 = K1.new.set(:bar=>456, :foo=>123)
>> p k1
>>
>> (I'm talking about real Struct here, not OpenStruct etc)
>
> ruby-1.8.7-p334 :001 > K = Struct.new :id,:timestamp
>  => K
> ruby-1.8.7-p334 :002 > K[3,8]
>  => #<struct K id=3, timestamp=8>

Sorry, I just realized this is not what you want.
Although if you know the order of the attributes defined by the
Struct, you can build something upon this, transforming the hash into
an array in the appropriate order. Whether that's cleaner than your
solution is not so clear.

Jesus.

[toc] | [prev] | [next] | [standalone]


#3636

From7stud -- <bbxx789_05ss@yahoo.com>
Date2011-04-28 12:16 -0500
Message-ID<061feef934e8496634c868f2a00a4ff2@ruby-forum.com>
In reply to#3632
"Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post 
#995567:
> On Thu, Apr 28, 2011 at 7:00 PM, Brian Candler <b.candler@pobox.com>
> wrote:
>>  self
>> (I'm talking about real Struct here, not OpenStruct etc)
> ruby-1.8.7-p334 :001 > K = Struct.new :id,:timestamp
>  => K
> ruby-1.8.7-p334 :002 > K[3,8]
>  => #<struct K id=3, timestamp=8>
>
> Jesus.

Is there a hash anywhere in your code? How about this:


K1 = Struct.new :foo, :bar

class MyClass < K1
  def initialize(hash)
    super(*hash.values_at(:foo, :bar) )
  end
end

puts MyClass.new(bar: 456, foo: 123)


(stolen from: 
http://stackoverflow.com/questions/2680523/dry-ruby-initialization-with-hash-argument)

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#3637

FromJesús Gabriel y Galán <jgabrielygalan@gmail.com>
Date2011-04-28 12:19 -0500
Message-ID<BANLkTi=rTnHVOqphCJ7GfdfmqPY_uyQHHQ@mail.gmail.com>
In reply to#3636
On Thu, Apr 28, 2011 at 7:16 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:
> "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post
> #995567:
>> On Thu, Apr 28, 2011 at 7:00 PM, Brian Candler <b.candler@pobox.com>
>> wrote:
>>>  self
>>> (I'm talking about real Struct here, not OpenStruct etc)
>> ruby-1.8.7-p334 :001 > K = Struct.new :id,:timestamp
>>  => K
>> ruby-1.8.7-p334 :002 > K[3,8]
>>  => #<struct K id=3, timestamp=8>
>>
>> Jesus.
>
> Is there a hash anywhere in your code? How about this:

Yep, I know. How about this?

ruby-1.8.7-p334 :014 > h = {:id => 3, :timestamp => 6}
 => {:timestamp=>6, :id=>3}
ruby-1.8.7-p334 :043 > K = Struct.new :id, :timestamp do
ruby-1.8.7-p334 :044 >     def self.from_hash h
ruby-1.8.7-p334 :045?>     self[*h.values_at(*K.members.map {|m| m.to_sym})]
ruby-1.8.7-p334 :046?>     end
ruby-1.8.7-p334 :047?>   end
 => K
ruby-1.8.7-p334 :048 > K.from_hash h
 => #<struct K id=3, timestamp=6>

Jesus.

[toc] | [prev] | [next] | [standalone]


#3654

FromBrian Candler <b.candler@pobox.com>
Date2011-04-28 15:24 -0500
Message-ID<69c1ac462d5ff72621121fcf6c8135ec@ruby-forum.com>
In reply to#3631
Thanks for all the feedback, at least I know I hadn't missed something 
obvious :-)

I found an interesting thread here too:
http://www.ruby-forum.com/topic/67759

Cheers,

Brian.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#3657

FromJoel VanderWerf <joelvanderwerf@gmail.com>
Date2011-04-28 15:50 -0500
Message-ID<4DB9D327.9000905@gmail.com>
In reply to#3654
On 04/28/2011 01:24 PM, Brian Candler wrote:
> Thanks for all the feedback, at least I know I hadn't missed something
> obvious :-)
>
> I found an interesting thread here too:
> http://www.ruby-forum.com/topic/67759

Tangentially, I wonder if anything like the following (very rough proof 
of concept) has been used instead of Struct.

class Hash
   def structify!
     keys.each do |key|
       class << self; self; end.class_eval do
         define_method key do
           fetch key
         end
         define_method "#{key}=" do |val|
           store key, val
         end
       end
     end
   end
end

h = {:foo => 1, :bar => 2}
h.structify!
p h.foo # 1
h.foo = 3
p h.foo # 3
p h     # {:foo=>3, :bar=>2}
p h.oof # undefined

[toc] | [prev] | [next] | [standalone]


#3683

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-29 03:06 -0500
Message-ID<BANLkTikR=wRKtkfe46=j+apo9bLRR-CHgQ@mail.gmail.com>
In reply to#3657
On Thu, Apr 28, 2011 at 10:50 PM, Joel VanderWerf
<joelvanderwerf@gmail.com> wrote:

> Tangentially, I wonder if anything like the following (very rough proof of
> concept) has been used instead of Struct.
>
> class Hash
>  def structify!
>    keys.each do |key|
>      class << self; self; end.class_eval do
>        define_method key do
>          fetch key
>        end
>        define_method "#{key}=" do |val|
>          store key, val
>        end
>      end
>    end
>  end
> end
>
> h = {:foo => 1, :bar => 2}
> h.structify!
> p h.foo # 1
> h.foo = 3
> p h.foo # 3
> p h     # {:foo=>3, :bar=>2}
> p h.oof # undefined

irb(main):004:0> h = {:foo => 1, :bar => 2}
=> {:foo=>1, :bar=>2}
irb(main):005:0> o = OpenStruct.new(h)
=> #<OpenStruct foo=1, bar=2>
irb(main):006:0> o.foo
=> 1
irb(main):007:0> o.bar
=> 2

Well, here we differ

irb(main):008:0> o.oof
=> nil

But the issue with your approach is that it is not dynamic.  Keys
added or removed after call to #structify! will not be taken care of.
A more dynamic approach would be

module HashStruct
  def method_missing(s,*a,&b)
    case
    when a.empty? && key?(s)
      self[s]
    when a.size == 1 && /\A(.+)=\z/ =~ s
      self[$1.to_sym] = a.first
    else
      super
    end
  end
end

h.extend HashStruct
h.foo

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#3702

FromJoel VanderWerf <joelvanderwerf@gmail.com>
Date2011-04-29 10:27 -0500
Message-ID<4DBAD8F0.8000202@gmail.com>
In reply to#3683
On 04/29/2011 01:06 AM, Robert Klemme wrote:
> On Thu, Apr 28, 2011 at 10:50 PM, Joel VanderWerf
> <joelvanderwerf@gmail.com>  wrote:
>
>> Tangentially, I wonder if anything like the following (very rough proof of
>> concept) has been used instead of Struct.
>>
>> class Hash
>>   def structify!
>>     keys.each do |key|
>>       class<<  self; self; end.class_eval do
>>         define_method key do
>>           fetch key
>>         end
>>         define_method "#{key}=" do |val|
>>           store key, val
>>         end
>>       end
>>     end
>>   end
>> end
>>
>> h = {:foo =>  1, :bar =>  2}
>> h.structify!
>> p h.foo # 1
>> h.foo = 3
>> p h.foo # 3
>> p h     # {:foo=>3, :bar=>2}
>> p h.oof # undefined
>
> irb(main):004:0>  h = {:foo =>  1, :bar =>  2}
> =>  {:foo=>1, :bar=>2}
> irb(main):005:0>  o = OpenStruct.new(h)
> =>  #<OpenStruct foo=1, bar=2>
> irb(main):006:0>  o.foo
> =>  1
> irb(main):007:0>  o.bar
> =>  2
>
> Well, here we differ
>
> irb(main):008:0>  o.oof
> =>  nil
>
> But the issue with your approach is that it is not dynamic.  Keys
> added or removed after call to #structify! will not be taken care of.
> A more dynamic approach would be

That's intended: #structify is supposed to turn a hash into something 
that looks like a Struct, not an OpenStruct.

[toc] | [prev] | [next] | [standalone]


#3686

FromBrian Candler <b.candler@pobox.com>
Date2011-04-29 03:22 -0500
Message-ID<03b8b18259c6ce145336bdbb11ba2e1f@ruby-forum.com>
In reply to#3657
Joel VanderWerf wrote in post #995632:
> Tangentially, I wonder if anything like the following (very rough proof
> of concept) has been used instead of Struct.

That's neat. However you'd have to make sure you've set a value for 
every key of interest, including defaults, before calling "structify!"

How about this variation:

class StructHash < Hash
  def initialize(h = {})
    replace(self.class::DEFAULTS.merge(h))
  end
end

def StructHash(defaults)
  k = Class.new(StructHash)
  k.const_set(:DEFAULTS, defaults)
  defaults.keys.each do |key|
    k.class_eval do
      define_method key do
        fetch key
      end
      define_method "#{key}=" do |val|
        store key, val
      end
    end
  end
  k
end

Foo = StructHash(:foo=>123, :bar=>456)
f = Foo.new(:foo=>0)
p f
p f.foo
p f.bar

I'm usually not a fan of subclassing core types, but I could be 
persuaded here.

Perhaps the initialize function should look like this instead:

class StructHash < Hash
  def initialize(h = {})
    replace(self.class::DEFAULTS)
    h.each { |k,v| send("#{k}=", v) }
  end
end

It's not as fast, but it will catch errors if you try to set 
non-existent members, and it lets you use symbols and strings 
interchangeably.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.ruby


csiph-web