Initialize Struct from Hash

B

Brian Candler

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.
 
J

Jesús Gabriel y Galán

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 =3D Struct.new :foo, :bar

module FixStruct
=A0def set(h)
=A0 =A0h.each { |k,v| self[k] =3D v }
=A0 =A0self
=A0end
end

class K1
=A0include FixStruct
end

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

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

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

Jesus.
 
J

Jesús Gabriel y Galán

2011/4/28 Jes=FAs Gabriel y Gal=E1n said:
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 =3D Struct.new :foo, :bar

module FixStruct
=A0def set(h)
=A0 =A0h.each { |k,v| self[k] =3D v }
=A0 =A0self
=A0end
end

class K1
=A0include FixStruct
end

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

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

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

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.
 
7

7stud --

t =

#995567:
self
(I'm talking about real Struct here, not OpenStruct etc)
ruby-1.8.7-p334 :001 > K =3D Struct.new :id,:timestamp
=3D> K
ruby-1.8.7-p334 :002 > K[3,8]
=3D> #<struct K id=3D3, timestamp=3D8>

Jesus.

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


K1 =3D 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-h=
ash-argument)

-- =

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

Jesús Gabriel y Galán

Jes=FAs Gabriel y Gal=E1n said:
self
(I'm talking about real Struct here, not OpenStruct etc)
ruby-1.8.7-p334 :001 > K =3D Struct.new :id,:timestamp
=3D> K
ruby-1.8.7-p334 :002 > K[3,8]
=3D> #<struct K id=3D3, timestamp=3D8>

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 =3D {:id =3D> 3, :timestamp =3D> 6}
=3D> {:timestamp=3D>6, :id=3D>3}
ruby-1.8.7-p334 :043 > K =3D 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
=3D> K
ruby-1.8.7-p334 :048 > K.from_hash h
=3D> #<struct K id=3D3, timestamp=3D6>

Jesus.
 
J

Joel VanderWerf

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
 
R

Robert Klemme

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

class Hash
=A0def structify!
=A0 =A0keys.each do |key|
=A0 =A0 =A0class << self; self; end.class_eval do
=A0 =A0 =A0 =A0define_method key do
=A0 =A0 =A0 =A0 =A0fetch key
=A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0define_method "#{key}=3D" do |val|
=A0 =A0 =A0 =A0 =A0store key, val
=A0 =A0 =A0 =A0end
=A0 =A0 =A0end
=A0 =A0end
=A0end
end

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

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

Well, here we differ

irb(main):008:0> o.oof
=3D> 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
when a.size =3D=3D 1 && /\A(.+)=3D\z/ =3D~ s
self[$1.to_sym] =3D a.first
else
super
end
end
end

h.extend HashStruct
h.foo

Kind regards

robert

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

Brian Candler

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.
 
J

Joel VanderWerf

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top