Hash default value

Hello,

I'm bitten by a hash default value problem. I wanted to avoid the

myHash[a] = {} if !myHash[a]; myHash[a][“blue”] = 1
pattern by using hash default values, but i get an unexpected (for
me) result:

irb(main):001:0> myHash = {}
{}
irb(main):002:0> myHash[“blue”][“red”] = 1
NameError: undefined method `[]=’ for nil
from (irb):2
irb(main):003:0> myOtherHash = Hash.new({})
{}
irb(main):004:0> myOtherHash[“blue”][“red”] = 1
1
irb(main):005:0> p myOtherHash
{}
nil

i expected that myOtherHash would be {“blue”=>{“red”=>1}}

what am i missing here? is there a workaround to the ugly syntax i said
i want to avoid? (you have the right to say “store your data in a decent
manner”)

emmanuel

PS: don’t think it matters, but i’m running ruby 1.6.7 (2002-03-01)
[i586-mswin32]

Hi –

Hello,

I'm bitten by a hash default value problem. I wanted to avoid the

myHash[a] = {} if !myHash[a]; myHash[a][“blue”] = 1
pattern by using hash default values, but i get an unexpected (for
me) result:

irb(main):001:0> myHash = {}
{}
irb(main):002:0> myHash[“blue”][“red”] = 1
NameError: undefined method `=’ for nil
from (irb):2
irb(main):003:0> myOtherHash = Hash.new({})
{}
irb(main):004:0> myOtherHash[“blue”][“red”] = 1
1
irb(main):005:0> p myOtherHash
{}
nil

i expected that myOtherHash would be {“blue”=>{“red”=>1}}

No, there’s no autovivification in the Perl manner :slight_smile:

what am i missing here? is there a workaround to the ugly syntax i said
i want to avoid? (you have the right to say “store your data in a decent
manner”)

The most common technique for this is:

my_hash = {}
my_hash[“blue”] ||= {} # init to hash if not init’d
my_hash[“blue”][“red”] = 1

which can be trimmed down to:

my_hash = {}
(my_hash[“blue”] ||= {})[“red”] = 1

David

···

On Tue, 12 Aug 2003, Emmanuel Touzery wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

irb(main):003:0> myOtherHash = Hash.new({})

You must use the block form

svg% ruby -e 'has = Hash.new {|h,k| h[k]={}}; has["blue"]["red"] = 1; p has'
{"blue"=>{"red"=>1}}
svg%

PS: don't think it matters, but i'm running ruby 1.6.7 (2002-03-01)

it will not work with 1.6.7

Guy Decoux

In article 3F38FA17.1080008@wanadoo.fr,

Hello,

I’m bitten by a hash default value problem. I wanted to avoid the
myHash[a] = {} if !myHash[a]; myHash[a][“blue”] = 1
pattern by using hash default values, but i get an unexpected (for
me) result:

irb(main):001:0> myHash = {}
{}
irb(main):002:0> myHash[“blue”][“red”] = 1
NameError: undefined method `=’ for nil
from (irb):2
irb(main):003:0> myOtherHash = Hash.new({})
{}
irb(main):004:0> myOtherHash[“blue”][“red”] = 1
1
irb(main):005:0> p myOtherHash
{}
nil

i expected that myOtherHash would be {“blue”=>{“red”=>1}}

what am i missing here? is there a workaround to the ugly syntax i said
i want to avoid? (you have the right to say “store your data in a decent
manner”)

In recent rubys (I tried on 1.8) you can say

myHash = Hash.new { |h, k| h[k] = Hash.new }

so that the newly created sub-hash is installed in the original hash.

This behaves as you want…

myHash[‘blue’][‘red’] = 1
=> 1
myHash[‘blue’][‘green’] = 2
=> 2
myHash[‘crimson’][‘king’] = 69
=> 69
p myHash
{“crimson”=>{“king”=>69}, “blue”=>{“green”=>2, “red”=>1}}

Hope this helps,

Mike

···

Emmanuel Touzery emmanuel.touzery@wanadoo.fr wrote:

mike@stok.co.uk | The “`Stok’ disclaimers” apply.
http://www.stok.co.uk/~mike/ | GPG PGP Key 1024D/059913DA
mike@exegenix.com | Fingerprint 0570 71CD 6790 7C28 3D60
http://www.exegenix.com/ | 75D2 9EC4 C1C0 0599 13DA

dblack@superlink.net wrote:

i expected that myOtherHash would be {“blue”=>{“red”=>1}}

No, there’s no autovivification in the Perl manner :slight_smile:

This not quiet true (for 1.8) - see Guy’s post. If you
want full autovivification your best bet is to sub-class
Hash

···

class AutoVHash < Hash
def initialize
end
def default(key)
self[key] = self.class.new
end
end

autovhsh = AutoVHash.new

autovhsh[1][2][3] = 4
p autovhsh[1][2][3] # => 4

or if you like puzzles


body = proc {|hsh,key| hsh[key] = Hash.new &body }
autovhsh = Hash.new &body

autovhsh[1][2][3] = 4
p autovhsh[1][2][3] # => 4

/Christoph

Hi –

···

On Wed, 13 Aug 2003, Christoph R. wrote:

dblack@superlink.net wrote:

i expected that myOtherHash would be {“blue”=>{“red”=>1}}

No, there’s no autovivification in the Perl manner :slight_smile:

This not quiet true (for 1.8) - see Guy’s post.

The block default value technique is vivification, but it’s not auto
:slight_smile: That’s the thing: Emmanuel’s expectation was Perl-style, whereas
in Ruby you have to supply the vivification routine yourself.

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Christoph R. wrote:

or if you like puzzles


body = proc {|hsh,key| hsh[key] = Hash.new &body }
autovhsh = Hash.new &body

autovhsh[1][2][3] = 4
p autovhsh[1][2][3] # => 4

Like puzzles, do you?

$y =
proc { |f|
proc { |x|
f[x]
}[proc { |x|
proc { |z,w|
f[x][z,w]
}
}]
}

def y(f)
proc do |z, w|
f[y(f)][z, w]
end
end

gen2 = proc { |f|
proc { |h, k|
h[k] = Hash.new &f
}
}

h1 = Hash.new &(y(gen2))
h2 = Hash.new &($y[gen2])

p “Using h1”
p h1[‘1’]
p h1[‘a’][‘b’][‘c’]

p “Using h2”

p h2[‘1’]
p h2[‘a’][‘b’][‘c’][‘d’][4]

Fun-ctional programming with Ruby.

  • Dan