An hash of arrays, how to use?

Hello everybody!

I have a problem with an Hash of Arrays.

ruby-1.9.2-p136 :001 > x = Hash.new Array.new
=> {}
ruby-1.9.2-p136 :002 > x[“test”] << “test”
=> [“test”]
ruby-1.9.2-p136 :003 > x
=> {}
ruby-1.9.2-p136 :004 > x.keys
=> []
ruby-1.9.2-p136 :005 > x[“test”]
=> [“test”]
ruby-1.9.2-p136 :006 >

Why is keys empty?

What I am trying to do is build a hash of arrays from an XML file:

  b = Hash.new(Array.new)
  h = XmlSimple.xml_in self.raw

  h["data"][0]["bank"][0]["item"].each do |item|
    b[item["cat"]] << item["name"]
  end

This is the code, but b has like the test in irb no keys, or seems to
be empty, unless you dont know the key i advance, but that is
impossible, since the item[“cat”] is dynamic and it is known of 10
categories at the moment, but it is also known that the number of
categories will go up in the future, so I want to do it future-proof
already today :smiley:

Thank you in Advance
Norbert

On Feb 11, 9:09pm, Norbert M. [email protected] wrote:

ruby-1.9.2-p136 :004 > x.keys
=> []
ruby-1.9.2-p136 :005 > x[“test”]
=> [“test”]
ruby-1.9.2-p136 :006 >

Why is keys empty?

because you’ve never actually done x[“test”]= …
(all you’re doing is modifying the hash’s default value)

Fred

2011/2/11 Frederick C. [email protected]:

On Feb 11, 9:09pm, Norbert M. [email protected] wrote:

Why is keys empty?

because you’ve never actually done x[“test”]= …
(all you’re doing is modifying the hash’s default value)

OK, now I tried the following

  b = Hash.new(Array.new)
  h = XmlSimple.xml_in self.raw

  h["data"][0]["bank"][0]["item"].each do |item|
    if b.key? item["cat"]
      b[item["cat"]] = item
    else
      b[item["cat"]] << item
    end
  end

  pp b.keys

but b.keys is still empty…

On Sat, Feb 12, 2011 at 9:25 AM, Norbert M.
[email protected] wrote:

end

pp b.keys

call me stupid… But with above marked change it works as I want it
to behave :smiley:

try this,

b = Hash.new {|h,k| h[k]=[]}
h = XmlSimple.xml_in self.raw

h[“data”][0][“bank”][0][“item”].each do |item|
b[item[“cat”]] << item
end

best regards -botp

On Feb 12, 12:47am, Norbert M. [email protected] wrote:

OK, now I tried the following

b = Hash.new(Array.new)
h = XmlSimple.xml_in self.raw

h[“data”][0][“bank”][0][“item”].each do |item|
if b.key? item[“cat”]
b[item[“cat”]] = item
else
b[item[“cat”]] << item

You’re still essentially committing the same error - you’re only
setting b[item[‘cat’] if there is already an entry for b[item[‘cat’]]
Did you really mean to use a hash with a single array as its default
value? (The default value stuff doesn’t seem to be helping you at all,
just muddying the waters

Fred

2011/2/12 Frederick C. [email protected]:

On Feb 12, 12:47am, Norbert M. [email protected] wrote:

2011/2/11 Frederick C. [email protected]:

just muddying the waters
As I mentioned above, my version did it after I introduced “if !b.key?
[…]”. But I will test the code that botp gave me too, seems to be
more DRY :smiley:

OK, now I tried the following

b = Hash.new(Array.new)
h = XmlSimple.xml_in self.raw

h[“data”][0][“bank”][0][“item”].each do |item|
delete line> if b.key? item[“cat”]
add line> if !b.key? item[“cat”]
b[item[“cat”]] = item
else
b[item[“cat”]] << item
end
end

pp b.keys

call me stupid… But with above marked change it works as I want it
to behave :smiley:

Thanks for the help!

Norbert

Hi Norbert:

An idiom that I use frequently is:

h = {}
(h[key] ||= []) << item

which essentially says "if hash h[key] is nil, create an empty array for
that key. then push item onto the array. for example:

h = {}
=> {}

(h[“cats”] ||= []) << “Jellicle”
=> [“Jellicle”]

(h[“cats”] ||= []) << “Mr. Mistoffelees”
=> [“Jellicle”, “Mr. Mistoffelees”]

Is that what you were looking for?

  • ff