Non-destructive merging of hashes in array

Hi, I have an array of hashes. The keys in the hashes represent the same things.

eg:

h1 = {:rabbits => 5}
h2 = {:rabbits => 10}

bunnies = [h1, h2]

I want to end up with this:

{:rabbits => 15}

It's a trivial task, but what's the quickest way to get there? I'm
certain it can be done on one line.

I know there's a Hash#update, but it appears that it would just
replace the 10 with 5, or vice versa, rather than adding them up.

This works, but it seems clunky:

hashes.each do |h|
  h.each do |k, v|
    if new_hash[k]
      new_hash[k] += v
    else
      new_hash[k] = v
    end
  end
end

It can be crammed all onto one line, too, but there must be a nicer way.

···

--
Giles Bowkett
http://www.gilesgoatboy.org


How's this:

hashes.inject({}) {|result, hash| result.merge(hash) {|k, e, v| (result[k]

0) + v}}

or similarly:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| result[k] + v}}

Mushfeq.

···

On 3/12/07, Giles Bowkett <gilesb@gmail.com> wrote:

Hi, I have an array of hashes. The keys in the hashes represent the same
things.

eg:

h1 = {:rabbits => 5}
h2 = {:rabbits => 10}

bunnies = [h1, h2]

I want to end up with this:

{:rabbits => 15}

It's a trivial task, but what's the quickest way to get there? I'm
certain it can be done on one line.

I know there's a Hash#update, but it appears that it would just
replace the 10 with 5, or vice versa, rather than adding them up.

This works, but it seems clunky:

hashes.each do |h|
  h.each do |k, v|
    if new_hash[k]
      new_hash[k] += v
    else
      new_hash[k] = v
    end
  end
end

It can be crammed all onto one line, too, but there must be a nicer way.

--
Giles Bowkett
http://www.gilesgoatboy.org
http://gilesbowkett.blogspot.com
http://giles.tumblr.com/

<snip>

It can be crammed all onto one line, too, but there must be a nicer way.

Maybe but I like my 5 lines already :wink:

r=Hash.new{|h,k| h[k]=0}
hashes.each{
  >hash>
  hash.each{ |k,v| r[k]+= v }
}

HTH
Robert

···

On 3/13/07, Giles Bowkett <gilesb@gmail.com> wrote:

--
Giles Bowkett
http://www.gilesgoatboy.org
http://gilesbowkett.blogspot.com
http://giles.tumblr.com/

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important things.
-Anonymous

(SORRY IF THIS GETS POSTED TWICE)

The inject/merge solutions are good, but they are one trick ponies.
How about something like:

   OpenCollection[h1, h2].rabbits.sum

It shouldn't be too hard to write:

   require 'ostruct'

   class OpenCollection
     class << self ; alias : :new ; end
     def initialize(*hashes)
       @opens = hashes.collect { |h| OpenStruct.new(h) }
     end

     def method_missing(sym, *args)
       @opens.collect{ |o| o.send(sym) }
     end
   end

Actually, I'd use Facets OpenObject instead OpenStruct myself, but
that's just me. You'll also need:

  require 'facets/core/enumerable/sum'

For fun, here's a one line version (more or less):

   require 'facets/more/functor'

   oc = Functor.new([h1,h2]){|s,m| m.map{|h| h[s]}}

   oc.rabbits.sum

T.

···

On Mar 12, 10:01 pm, "Giles Bowkett" <gil...@gmail.com> wrote:

Hi, I have an array of hashes. The keys in the hashes represent the same things.

eg:

h1 = {:rabbits => 5}
h2 = {:rabbits => 10}

bunnies = [h1, h2]

I want to end up with this:

{:rabbits => 15}

It's a trivial task, but what's the quickest way to get there? I'm
certain it can be done on one line.

I know there's a Hash#update, but it appears that it would just
replace the 10 with 5, or vice versa, rather than adding them up.

This works, but it seems clunky:

hashes.each do |h|
  h.each do |k, v|
    if new_hash[k]
      new_hash[k] += v
    else
      new_hash[k] = v
    end
  end
end

It can be crammed all onto one line, too, but there must be a nicer way.

Giles Bowkett wrote:

Hi, I have an array of hashes. The keys in the hashes represent the same
things.

eg:

h1 = {:rabbits => 5}
h2 = {:rabbits => 10}

bunnies = [h1, h2]

I want to end up with this:

{:rabbits => 15}

res = Hash.new(0) # to give the new keys a sensible default
bunnies.each {|arr| arr.each { | key, value | res[key] += value }}

P.S.: I thought about
res = {}
bunnies.each {|arr| arr.each { | key, value | (res[key]||=0) += value }}

but (res[key]||=0) is not a lvalue. It was even considered a syntax
error with the helpfull error message of:

   syntax error, unexpected tOP_ASGN, expecting '}'

···

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

You can even do:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| e + v}}

Mushfeq.

···

On 3/12/07, Mushfeq Khan <mushfeq.khan@gmail.com> wrote:

How's this:

hashes.inject({}) {|result, hash| result.merge(hash) {|k, e, v| (result[k]
>> 0) + v}}

or similarly:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| result[k] + v}}

Mushfeq.

On 3/12/07, Giles Bowkett <gilesb@gmail.com> wrote:
>
> Hi, I have an array of hashes. The keys in the hashes represent the same
> things.
>
> eg:
>
> h1 = {:rabbits => 5}
> h2 = {:rabbits => 10}
>
> bunnies = [h1, h2]
>
> I want to end up with this:
>
> {:rabbits => 15}
>
> It's a trivial task, but what's the quickest way to get there? I'm
> certain it can be done on one line.
>
> I know there's a Hash#update, but it appears that it would just
> replace the 10 with 5, or vice versa, rather than adding them up.
>
> This works, but it seems clunky:
>
> hashes.each do |h|
> h.each do |k, v|
> if new_hash[k]
> new_hash[k] += v
> else
> new_hash[k] = v
> end
> end
> end
>
> It can be crammed all onto one line, too, but there must be a nicer way.
>
> --
> Giles Bowkett
> http://www.gilesgoatboy.org
> http://gilesbowkett.blogspot.com
> http://giles.tumblr.com/
>

How's this:

hashes.inject({}) {|result, hash| result.merge(hash) {|k, e, v| (result[k]
>> 0) + v}}

or similarly:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| result[k] + v}}

I bow to superior technique, the merge block form is particularly nice.

Mushfeq.

Robert

···

On 3/13/07, Mushfeq Khan <mushfeq.khan@gmail.com> wrote:
--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important things.
-Anonymous

Tom Facet is a great thing and I do not fail to point to it regulary.
But sometimes I feel we have to flex our muscles in pure Ruby before
we shall use libraries, even excellent ones like Facets, just to
understand everything a little better.

This all does not mean that your post is not very valuable, I just
want to warn from the "Pull In A Library before Do Some Thinking"
approach.

i fear that this approach hurts the user as much as the library.

Cheers
Robert

···

On 3/13/07, Trans <transfire@gmail.com> wrote:

On Mar 12, 10:01 pm, "Giles Bowkett" <gil...@gmail.com> wrote:
> Hi, I have an array of hashes. The keys in the hashes represent the same things.
>
> eg:
>
> h1 = {:rabbits => 5}
> h2 = {:rabbits => 10}
>
> bunnies = [h1, h2]
>
> I want to end up with this:
>
> {:rabbits => 15}
>
> It's a trivial task, but what's the quickest way to get there? I'm
> certain it can be done on one line.
>
> I know there's a Hash#update, but it appears that it would just
> replace the 10 with 5, or vice versa, rather than adding them up.
>
> This works, but it seems clunky:
>
> hashes.each do |h|
> h.each do |k, v|
> if new_hash[k]
> new_hash[k] += v
> else
> new_hash[k] = v
> end
> end
> end
>
> It can be crammed all onto one line, too, but there must be a nicer way.

(SORRY IF THIS GETS POSTED TWICE)

The inject/merge solutions are good, but they are one trick ponies.
How about something like:

   OpenCollection[h1, h2].rabbits.sum

It shouldn't be too hard to write:

   require 'ostruct'

   class OpenCollection
     class << self ; alias : :new ; end
     def initialize(*hashes)
       @opens = hashes.collect { |h| OpenStruct.new(h) }
     end

     def method_missing(sym, *args)
       @opens.collect{ |o| o.send(sym) }
     end
   end

Actually, I'd use Facets OpenObject instead OpenStruct myself, but
that's just me. You'll also need:

  require 'facets/core/enumerable/sum'

For fun, here's a one line version (more or less):

   require 'facets/more/functor'

   oc = Functor.new([h1,h2]){|s,m| m.map{|h| h[s]}}

   oc.rabbits.sum

T.

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

That's a very concise solution. It seems to be working, although I
need to verify that.

···

On 3/12/07, Mushfeq Khan <mushfeq.khan@gmail.com> wrote:

You can even do:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| e + v}}

--
Giles Bowkett
http://www.gilesgoatboy.org

http://giles.tumblr.com/

Oh, you beat me to it. I might add, though, that the parameters in
the initializer block can be safely removed:

hashes.inject(Hash.new {0}) {|memo, hash| memo.merge(hash) {|k,e,v| e
+ v}}

Harrison Reiser

···

On Mar 12, 8:39 pm, "Mushfeq Khan" <mushfeq.k...@gmail.com> wrote:

You can even do:

hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
e, v| e + v}}

I understand what youre saying --and I waited on posting this until
others gave solutions. Though in this particular case I think there's
some pretty good meat here, ie. the OpenCollection class I literally
just made up on the spot. Of course that still leaves Enumerable#sum,
but that's rather straight forward: a.inject(0){|s,n| s+=n;s}.

The Functor was just a little playful plug. If you've ever seen the
functor code you know it's a generalization of what the OpenCollection
class is doing. I actually would like to see Functor included in
Ruby's standard lib. But I haven't been able to convince Matz of it's
usefulness. So I try to publicly use it when ever I get the chance.

HTH,
T.

···

On Mar 13, 8:40 am, "Robert Dober" <robert.do...@gmail.com> wrote:

On 3/13/07, Trans <transf...@gmail.com> wrote:

> On Mar 12, 10:01 pm, "Giles Bowkett" <gil...@gmail.com> wrote:
> > Hi, I have an array of hashes. The keys in the hashes represent the same things.

> > eg:

> > h1 = {:rabbits => 5}
> > h2 = {:rabbits => 10}

> > bunnies = [h1, h2]

> > I want to end up with this:

> > {:rabbits => 15}

> > It's a trivial task, but what's the quickest way to get there? I'm
> > certain it can be done on one line.

> > I know there's a Hash#update, but it appears that it would just
> > replace the 10 with 5, or vice versa, rather than adding them up.

> > This works, but it seems clunky:

> > hashes.each do |h|
> > h.each do |k, v|
> > if new_hash[k]
> > new_hash[k] += v
> > else
> > new_hash[k] = v
> > end
> > end
> > end

> > It can be crammed all onto one line, too, but there must be a nicer way.

> (SORRY IF THIS GETS POSTED TWICE)

> The inject/merge solutions are good, but they are one trick ponies.
> How about something like:

> OpenCollection[h1, h2].rabbits.sum

> It shouldn't be too hard to write:

> require 'ostruct'

> class OpenCollection
> class << self ; alias : :new ; end
> def initialize(*hashes)
> @opens = hashes.collect { |h| OpenStruct.new(h) }
> end

> def method_missing(sym, *args)
> @opens.collect{ |o| o.send(sym) }
> end
> end

> Actually, I'd use Facets OpenObject instead OpenStruct myself, but
> that's just me. You'll also need:

> require 'facets/core/enumerable/sum'

> For fun, here's a one line version (more or less):

> require 'facets/more/functor'

> oc = Functor.new([h1,h2]){|s,m| m.map{|h| h[s]}}

> oc.rabbits.sum

> T.

Tom Facet is a great thing and I do not fail to point to it regulary.
But sometimes I feel we have to flex our muscles in pure Ruby before
we shall use libraries, even excellent ones like Facets, just to
understand everything a little better.

This all does not mean that your post is not very valuable, I just
want to warn from the "Pull In A Library before Do Some Thinking"
approach.

i fear that this approach hurts the user as much as the library.

and my humble contribution gives:

     hashes.inject{|memo, hash| memo.merge(hash) {|k,e,v| e+ v}}

which is - as Gilles guessed correctly - a concise oneliner, boy I love Ruby.

Robert

···

On 3/13/07, Harrison Reiser <y2kbugxp90@gmail.com> wrote:

On Mar 12, 8:39 pm, "Mushfeq Khan" <mushfeq.k...@gmail.com> wrote:
> You can even do:
>
> hashes.inject(Hash.new {|h, k| 0}) {|result, hash| result.merge(hash) {|k,
> e, v| e + v}}

Oh, you beat me to it. I might add, though, that the parameters in
the initializer block can be safely removed:

hashes.inject(Hash.new {0}) {|memo, hash| memo.merge(hash) {|k,e,v| e
+ v}}

Harrison Reiser

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important things.
-Anonymous

OMG was I too rude, maybe? Probably just to stupid to really
understand your mail :frowning:
The good thing is though that I understand now what you wanted to tell us.
And I am one of the greatest fans of magic dot.
Thx and sorry.
Robert

···

On 3/13/07, Trans <transfire@gmail.com> wrote:

On Mar 13, 8:40 am, "Robert Dober" <robert.do...@gmail.com> wrote:
> On 3/13/07, Trans <transf...@gmail.com> wrote:
>
> > On Mar 12, 10:01 pm, "Giles Bowkett" <gil...@gmail.com> wrote:
> > > Hi, I have an array of hashes. The keys in the hashes represent the same things.
>
> > > eg:
>
> > > h1 = {:rabbits => 5}
> > > h2 = {:rabbits => 10}
>
> > > bunnies = [h1, h2]
>
> > > I want to end up with this:
>
> > > {:rabbits => 15}
>
> > > It's a trivial task, but what's the quickest way to get there? I'm
> > > certain it can be done on one line.
>
> > > I know there's a Hash#update, but it appears that it would just
> > > replace the 10 with 5, or vice versa, rather than adding them up.
>
> > > This works, but it seems clunky:
>
> > > hashes.each do |h|
> > > h.each do |k, v|
> > > if new_hash[k]
> > > new_hash[k] += v
> > > else
> > > new_hash[k] = v
> > > end
> > > end
> > > end
>
> > > It can be crammed all onto one line, too, but there must be a nicer way.
>
> > (SORRY IF THIS GETS POSTED TWICE)
>
> > The inject/merge solutions are good, but they are one trick ponies.
> > How about something like:
>
> > OpenCollection[h1, h2].rabbits.sum
>
> > It shouldn't be too hard to write:
>
> > require 'ostruct'
>
> > class OpenCollection
> > class << self ; alias : :new ; end
> > def initialize(*hashes)
> > @opens = hashes.collect { |h| OpenStruct.new(h) }
> > end
>
> > def method_missing(sym, *args)
> > @opens.collect{ |o| o.send(sym) }
> > end
> > end
>
> > Actually, I'd use Facets OpenObject instead OpenStruct myself, but
> > that's just me. You'll also need:
>
> > require 'facets/core/enumerable/sum'
>
> > For fun, here's a one line version (more or less):
>
> > require 'facets/more/functor'
>
> > oc = Functor.new([h1,h2]){|s,m| m.map{|h| h[s]}}
>
> > oc.rabbits.sum
>
> > T.
>
> Tom Facet is a great thing and I do not fail to point to it regulary.
> But sometimes I feel we have to flex our muscles in pure Ruby before
> we shall use libraries, even excellent ones like Facets, just to
> understand everything a little better.
>
> This all does not mean that your post is not very valuable, I just
> want to warn from the "Pull In A Library before Do Some Thinking"
> approach.
>
> i fear that this approach hurts the user as much as the library.

I understand what youre saying --and I waited on posting this until
others gave solutions. Though in this particular case I think there's
some pretty good meat here, ie. the OpenCollection class I literally
just made up on the spot. Of course that still leaves Enumerable#sum,
but that's rather straight forward: a.inject(0){|s,n| s+=n;s}.

The Functor was just a little playful plug. If you've ever seen the
functor code you know it's a generalization of what the OpenCollection
class is doing. I actually would like to see Functor included in
Ruby's standard lib. But I haven't been able to convince Matz of it's
usefulness. So I try to publicly use it when ever I get the chance.

HTH,
T.

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

The functor seems pretty cool. The sad thing is, I still have the most
primitive implementation possible in my actual code. The reason is, I
don't want to pop it in without fully understanding it.

The memo solution looks cleanest, that's really just a gut feeling
though. Let me just make sure I get it. Here it is:

hashes.inject{|memo, hash| memo.merge(hash) {|k,e,v| e+ v}}

Now partly it turns out my problem is slightly more complicated. It's
not an array of hashes; it's an array of objects which can return
hashes. So it basically looks like this:

def enter_output
  @items = get_the_items
  @happy_output_hash = {}
  # non-destructively merge all hashes within @items into one hash
  @items.each do |item|
    item.hash_within.each do |key, value|
      if @happy_output_hash[key]
        @happy_output_hash[key] += value
      else
        @happy_output_hash[key] = value
      end
    end
  end
end

(Code altered to enhance obviousness.)

Would the correct translation of the memo solution to accomodate this
be something like this?

items.inject{|happy_output_hash, item|
happy_output_hash.merge(item.hash_within) {|k,e,v| e + v}}

Also, what does the Functor solution actually do? That sounds Lispy,
which appeals to me, but I want to be sure the code is maintainable by
lesser mortals, such as, for example, me.

···

--
Giles Bowkett
http://www.gilesgoatboy.org

http://giles.tumblr.com/

On 3/13/07, Trans <transfire@gmail.com> wrote:

On Mar 13, 8:40 am, "Robert Dober" <robert.do...@gmail.com> wrote:
> On 3/13/07, Trans <transf...@gmail.com> wrote:
>
> > On Mar 12, 10:01 pm, "Giles Bowkett" <gil...@gmail.com> wrote:
> > > Hi, I have an array of hashes. The keys in the hashes represent the same things.
>
> > > eg:
>
> > > h1 = {:rabbits => 5}
> > > h2 = {:rabbits => 10}
>
> > > bunnies = [h1, h2]
>
> > > I want to end up with this:
>
> > > {:rabbits => 15}
>
> > > It's a trivial task, but what's the quickest way to get there? I'm
> > > certain it can be done on one line.
>
> > > I know there's a Hash#update, but it appears that it would just
> > > replace the 10 with 5, or vice versa, rather than adding them up.
>
> > > This works, but it seems clunky:
>
> > > hashes.each do |h|
> > > h.each do |k, v|
> > > if new_hash[k]
> > > new_hash[k] += v
> > > else
> > > new_hash[k] = v
> > > end
> > > end
> > > end
>
> > > It can be crammed all onto one line, too, but there must be a nicer way.
>
> > (SORRY IF THIS GETS POSTED TWICE)
>
> > The inject/merge solutions are good, but they are one trick ponies.
> > How about something like:
>
> > OpenCollection[h1, h2].rabbits.sum
>
> > It shouldn't be too hard to write:
>
> > require 'ostruct'
>
> > class OpenCollection
> > class << self ; alias : :new ; end
> > def initialize(*hashes)
> > @opens = hashes.collect { |h| OpenStruct.new(h) }
> > end
>
> > def method_missing(sym, *args)
> > @opens.collect{ |o| o.send(sym) }
> > end
> > end
>
> > Actually, I'd use Facets OpenObject instead OpenStruct myself, but
> > that's just me. You'll also need:
>
> > require 'facets/core/enumerable/sum'
>
> > For fun, here's a one line version (more or less):
>
> > require 'facets/more/functor'
>
> > oc = Functor.new([h1,h2]){|s,m| m.map{|h| h[s]}}
>
> > oc.rabbits.sum
>
> > T.
>
> Tom Facet is a great thing and I do not fail to point to it regulary.
> But sometimes I feel we have to flex our muscles in pure Ruby before
> we shall use libraries, even excellent ones like Facets, just to
> understand everything a little better.
>
> This all does not mean that your post is not very valuable, I just
> want to warn from the "Pull In A Library before Do Some Thinking"
> approach.
>
> i fear that this approach hurts the user as much as the library.

I understand what youre saying --and I waited on posting this until
others gave solutions. Though in this particular case I think there's
some pretty good meat here, ie. the OpenCollection class I literally
just made up on the spot. Of course that still leaves Enumerable#sum,
but that's rather straight forward: a.inject(0){|s,n| s+=n;s}.

The Functor was just a little playful plug. If you've ever seen the
functor code you know it's a generalization of what the OpenCollection
class is doing. I actually would like to see Functor included in
Ruby's standard lib. But I haven't been able to convince Matz of it's
usefulness. So I try to publicly use it when ever I get the chance.

HTH,
T.

The functor seems pretty cool. The sad thing is, I still have the most
primitive implementation possible in my actual code. The reason is, I
don't want to pop it in without fully understanding it.

The memo solution looks cleanest, that's really just a gut feeling
though. Let me just make sure I get it. Here it is:

hashes.inject{|memo, hash| memo.merge(hash) {|k,e,v| e+ v}}

Now partly it turns out my problem is slightly more complicated. It's
not an array of hashes; it's an array of objects which can return
hashes. So it basically looks like this:

def enter_output
  @items = get_the_items
  @happy_output_hash = {}
  # non-destructively merge all hashes within @items into one hash
  @items.each do |item|
    item.hash_within.each do |key, value|
      if @happy_output_hash[key]
        @happy_output_hash[key] += value
      else
        @happy_output_hash[key] = value
      end
    end
  end
end

(Code altered to enhance obviousness.)

Would the correct translation of the memo solution to accomodate this
be something like this?

items.inject{|happy_output_hash, item|
happy_output_hash.merge(item.hash_within) {|k,e,v| e + v}}

Should work. Or you could split it into two lines if it's easier to
read:

  hashes = items.collect{ |item| item.hash_within }

Also, what does the Functor solution actually do? That sounds Lispy,
which appeals to me, but I want to be sure the code is maintainable by
lesser mortals, such as, for example, me.

(Depending on what your doing exactly) the inject/merge is probably
just fine.

Functor is pretty straight forward. The basic definition is:

  class Functor
    def initialize(*objs, &block)
      @objs = objs
      @block = block
    end
    def method_missing(sym,*args)
      @block.call(@sym,*(@objs + args))
    end
  end

T.

···

On Mar 14, 4:49 pm, "Giles Bowkett" <gil...@gmail.com> wrote:

There is a subtle difference between

  hashes.inject(Hash.new {0}) {|memo, hash| memo.merge(hash) {|k,e,v| e + v}}
  hashes.inject{|memo, hash| memo.merge(hash) {|k,e,v| e + v}}

(and by extension, your suggestion above, which is a correct
adaptation of the latter).

The optional parameter to inject provides a "seed" value. If no seed value is
provided, the first element of the enumerable is used as the seed and the block
evaluated over the tail only. In most cases this won't matter, and using an
argumentless inject is hunky-dory. But there are two gotchas:

1) If the array is empty, providing no seed means that +nil+ will be used as
the seed value. Probably not what you want as you'll get a NoMethodError for
nil#merge.

2) If not all the hashes in the array have the same keys, you'll probably get
some NoMethodErrors for nil#+. That's the reason the seed hash in the former
version provides an explicit default value.

Jacob Fugal

···

On 3/14/07, Giles Bowkett <gilesb@gmail.com> wrote:

Would the correct translation of the memo solution to accomodate this
be something like this?

items.inject{|happy_output_hash, item|
happy_output_hash.merge(item.hash_within) {|k,e,v| e + v}}