[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

YAML obj merging

gga

3/10/2005 5:42:00 AM

I would like to have an object (think 2 hashes one inside the other)
that would get created from reading in two YAML descriptions and
merging them together.

I'm looking that given something like:
---
A:
v: valueA
---
B:
v: valueB
x: value
=====
C = A + B should result in:
C:
v: valueA # B's v: valueB discarded, overridden by valueA
x: value # B's x: value not discarded

Before I write my own stuff to do that sort of operation on a hash, I
was wondering if there were already some libs written to do something
similar or, put in another way, what would be the best ruby way of
doing so?

5 Answers

Dave Burt

3/10/2005 7:31:00 AM

0

"gga" <GGarramuno@aol.com> asked:
>I would like to have an object (think 2 hashes one inside the other)
> that would get created from reading in two YAML descriptions and
> merging them together.
>
> I'm looking that given something like:
> ---
> A:
> v: valueA
> ---
> B:
> v: valueB
> x: value
> =====
> C = A + B should result in:
> C:
> v: valueA # B's v: valueB discarded, overridden by valueA
> x: value # B's x: value not discarded
>
> Before I write my own stuff to do that sort of operation on a hash, I
> was wondering if there were already some libs written to do something
> similar or, put in another way, what would be the best ruby way of
> doing so?

Hi,

I may not completely understand what you're trying to do, but it seems to me
that it's just basic stuff that's built in to Hash; in particular, the
update method.

irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> input = YAML.load_stream <<END
irb(main):003:0" ---
irb(main):004:0" A:
irb(main):005:0" v: valueA
irb(main):006:0" ---
irb(main):007:0" B:
irb(main):008:0" v: valueB
irb(main):009:0" x: value
irb(main):010:0" END
=> #<YAML::Stream:0x28f1de8 @options={}, @documents=[{"A"=>{"v"=>"valueA"}},
{"B
"=>{"v"=>"valueB", "x"=>"value"}}]>
irb(main):011:0> a = input.documents[0]['A']
=> {"v"=>"valueA"}
irb(main):012:0> b = input.documents[1]['B']
=> {"v"=>"valueB", "x"=>"value"}
irb(main):013:0> c = b; c.update(a)
=> {"v"=>"valueA", "x"=>"value"}
irb(main):014:0> puts YAML.dump({'C' => c})
---
C:
v: valueA
x: value
=> nil

If you want, you can even add + to Hash:

irb(main):018:0> class Hash
irb(main):019:1> def +(other)
irb(main):020:2> other.update(self)
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> a + b
=> {"v"=>"valueA", "x"=>"value"}

Cheers,
Dave


gga

3/11/2005 6:50:00 AM

0

That's kind of what I want but not quite.
The problem is that I want hashes that contain other hashes to also
follow the same rule.

That is:
irb> a = { 'A' => { 'A1' => 'a' } }
irb> b = { 'A' => { 'B1' => 'b' } }
irb> b.merge(a)
{"A"=>{"A1"=>"a"}}

When what I really want is this to be done recursively, so that:
irb> a = { 'A' => { 'A1' => 'a' } }
irb> b = { 'A' => { 'B1' => 'b' } }
irb> b.mix(a) # ficticious method
{"A"=>{"A1"=>"a", "B1" => 'b' }}

I guess I probably need to add my own method to do this.

Trans

3/11/2005 9:48:00 PM

0

Ruby Facects has hash/weave. It does what you wish and a little more.

http://rubyforge.org/project...

T.

Florian Gross

3/13/2005 11:07:00 AM

0

gga wrote:

> The problem is that I want hashes that contain other hashes to also
> follow the same rule.
>
> That is:
> irb> a = { 'A' => { 'A1' => 'a' } }
> irb> b = { 'A' => { 'B1' => 'b' } }
> irb> b.merge(a)
> {"A"=>{"A1"=>"a"}}
>
> When what I really want is this to be done recursively, so that:
> irb> a = { 'A' => { 'A1' => 'a' } }
> irb> b = { 'A' => { 'B1' => 'b' } }
> irb> b.mix(a) # ficticious method
> {"A"=>{"A1"=>"a", "B1" => 'b' }}
>
> I guess I probably need to add my own method to do this.

I'm coming a bit late to this discussion (I've only spotted it after
I've read through this weeks' Ruby Weekly News), but here's my version:

class Hash
# Similar to Hash#merge, but this version works recursively so that
# nested Hashs will be merged as well. You can specify a collision
# block which will be invoked for conflicting items. (It takes the
# same argument list as the collision block supplied to Hash#merge:
# |hash, item1, item2|)
#
# Usage example:
# a = { "A" => { "A1" => "a" } }
# b = { "A" => { "B1" => "b" } }
# b.mix(a) # => {"A" => {"A1" => "a", "B1" => "b" }}
def mix(other, &collision_block)
merge_block = lambda do |hash, item1, item2|
if item1.is_a?(Hash) and item2.is_a?(Hash) then
item1.merge(item2, &merge_block)
elsif collision_block then
collision_block.call(hash, item1, item2)
else
item2
end
end

self.merge(other, &merge_block)
end
end

So basically the block that Hash#merge can take is insanely useful. I've
also used for just detecting Hash collisions in the past as well.

Shashank Date

3/13/2005 2:24:00 PM

0

Hi Florian,

Florian Gross wrote:

<snip>

> class Hash
> # Similar to Hash#merge, but this version works recursively so that
> # nested Hashs will be merged as well. You can specify a collision
> # block which will be invoked for conflicting items. (It takes the
> # same argument list as the collision block supplied to Hash#merge:
> |hash, item1, item2|)

I am not able to understand how the collision block thing will work
here. Can you please give an example? I tried this, but it did not work
like I expected:

b.mix(a){|key,old,nu| old} #=> {"A"=>{"B1"=>"b", "A1"=>"a"}}

> #
> # Usage example:
> # a = { "A" => { "A1" => "a" } }
> # b = { "A" => { "B1" => "b" } }
> # b.mix(a) # => {"A" => {"A1" => "a", "B1" => "b" }}
> def mix(other, &collision_block)
> merge_block = lambda do |hash, item1, item2|
> if item1.is_a?(Hash) and item2.is_a?(Hash) then
> item1.merge(item2, &merge_block)
> elsif collision_block then

When does this elsif get executed?

> collision_block.call(hash, item1, item2)
> else
> item2
> end
> end
>
> self.merge(other, &merge_block)
> end
> end
>
> So basically the block that Hash#merge can take is insanely useful. I've
> also used for just detecting Hash collisions in the past as well.

I did not know that Hash#merge could take a block. Thanks for pointing
it out.

-- shanko