N Dimensional Hash?

Hi

I'm looking for an object that behaves like a
n-dimensional hash. We'll start with two dimensions
to make this easy.

Lets say I have the data table

[
   %w{mini cooper red},
   %w{ford focus white},
   %w{ford mustang blue},
   %w{ab cd 1},
   %w{abc d 2},
   %w{a bcd 3},
]

Obviously I could build hash with:

  h = { "mini" => {},
        "ford" => {},
        "ab" => {},
        "abc" => {},
        "a" => {}
      }

  h["mini"] = { "cooper" => "red" }
  h["ford"] = { "focus" => "white", "mustang" => "blue" }
  h["ab"] = { "cd" => 1 }
  h["abc"] = { "d" => 2 }
  h["a"] = { "bcd" => 3 }

But this is painful.
Before I go and write a class to do this, is there
something that already does this. Like, maybe tuples,
which I don't understand.

...Hmm, I would describe this as a hierarchal struct with
named arguments.

h[:make=>"mini", :model=>"cooper"] #=> "red"
sames as:
h[:model=>"cooper", :make=>"mini"] #=> "red"

···

--
Jim Freeze
Hark, the Herald Tribune sings,
Advertising wondrous things.
    -- Tom Lehrer

"Jim Freeze" <jim@freeze.org> schrieb im Newsbeitrag
news:20040624134324.GA33615@freeze.org...

Hi

I'm looking for an object that behaves like a
n-dimensional hash. We'll start with two dimensions
to make this easy.

Lets say I have the data table

[
   %w{mini cooper red},
   %w{ford focus white},
   %w{ford mustang blue},
   %w{ab cd 1},
   %w{abc d 2},
   %w{a bcd 3},
]

Obviously I could build hash with:

  h = { "mini" => {},
        "ford" => {},
        "ab" => {},
        "abc" => {},
        "a" => {}
      }

  h["mini"] = { "cooper" => "red" }
  h["ford"] = { "focus" => "white", "mustang" => "blue" }
  h["ab"] = { "cd" => 1 }
  h["abc"] = { "d" => 2 }
  h["a"] = { "bcd" => 3 }

But this is painful.
Before I go and write a class to do this, is there
something that already does this. Like, maybe tuples,
which I don't understand.

..Hmm, I would describe this as a hierarchal struct with
named arguments.

h[:make=>"mini", :model=>"cooper"] #=> "red"
sames as:
h[:model=>"cooper", :make=>"mini"] #=> "red"

The typical idiom is:

ins = lambda {|h,k| h[k] = Hash.new &ins}
h = Hash.new &ins

like in

ins = lambda {|h,k| h[k] = Hash.new &ins}

=> #<Proc:0x10187298@(irb):1>

h=Hash.new &ins

=> {}

h["foo"]

=> {}

h["foo"]["bar"]

=> {}

h

=> {"foo"=>{"bar"=>{}}}

h["bar"]["test"] = "val"

=> "val"

h

=> {"foo"=>{"bar"=>{}}, "bar"=>{"test"=>"val"}}

Alternatively you can use Arrays as hash keys. But that depends on your
requirements.

Kind regards

    robert

An idiom I quite often use is...
a = Hash.new {|hash,key| hash[key]=Hash.new(0)}

Although in your case you may want something like...
a = Hash.new {|hash,key| hash[key]={}}
   or
a = Hash.new {|hash,key| hash[key]=Hash.new{|h,k| h[k]={}}}

I just love ruby.

Of course, you could fall back on the old "awk" standby...

key = %w{a b c d}

hash = {}
hash[key.join("#")]=whatever

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.

···

On Thu, 24 Jun 2004, Jim Freeze wrote:

Hi

I'm looking for an object that behaves like a
n-dimensional hash. We'll start with two dimensions
to make this easy.

"Robert Klemme" <bob.news@gmx.net> wrote in message

Alternatively you can use Arrays as hash keys.

I always thought that Arrays cannot be used as hash keys,
but after reading your email and re-reading Pickaxe (pg. 207)
I learnt that you _can_ use them albeit with a little
inconvenience: that you have to call Hash#rehash method
to reindex the hash whenever a key hash changes.

Thought I should share this insight here.
-- shanko

What about something like this?

require 'pp'

class Hash
    alias origindex []

    def [](*depth)
        return origindex( depth.first ) if depth.length == 1
        top = self
        depth.each {|level|
            top = top.origindex(level)
            break unless top.respond_to? '[]'
        }
        top
    end
end

h = { :mini => { :cooper => :red },
      :ford => { :focus => :white, :mustang => :blue }
}

pp h[]
pp h[:mini]
pp h[:mini, :cooper]
pp h[:something, :else, :another]

The error handling needs to be cleaned up so that this different []
behaves more identically to the original [], but maybe this is
something along the lines of what you're looking for?

Note that in this case, h[:something, :else] is different than
h[:else, :something]. It's something to start with, at least.

Alex

"Shashank Date" <sdate@everestkc.net> schrieb im Newsbeitrag
news:2k0g6fF15rigjU1@uni-berlin.de...

"Robert Klemme" <bob.news@gmx.net> wrote in message

> Alternatively you can use Arrays as hash keys.

I always thought that Arrays cannot be used as hash keys,
but after reading your email and re-reading Pickaxe (pg. 207)
I learnt that you _can_ use them albeit with a little
inconvenience: that you have to call Hash#rehash method
to reindex the hash whenever a key hash changes.

You should consider hash keys as constants. You can even enforce that by
inserting a frozen copy but that's not needed. Just don't reuse the
instance after you put it into the hash:

# arrays as keys
hash={}

[
   %w{mini cooper red},
   %w{ford focus white},
   %w{ford mustang blue},
   %w{ab cd 1},
   %w{abc d 2},
   %w{a bcd 3},
].each do |a|
  hash[a] = a.pop
end

p hash
p hash[%w{ford focus}]

# hash of hashes
i=lambda {|h,k| h[k] = Hash.new &i}
hash = Hash.new &i

[
   %w{mini cooper red},
   %w{ford focus white},
   %w{ford mustang blue},
   %w{ab cd 1},
   %w{abc d 2},
   %w{a bcd 3},
].each do |a|
  val = a.pop
  key = a.pop
  a.inject(hash) {|h,k| h[k]}[key] = val
end

p hash
p hash["ford"]["focus"]

Kind regards

    robert