|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
Hi all
PHP lets me easily create multidimensional hashes using the following syntax: x = array(); x["bla"]["some_key"] = true; Is Ruby not capable of doing this? x = [] x[:bla][:some_key] = true gives me a nil error! What wrong here? Thanks Josh -- Posted via http://www.ruby-forum.com/. |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
Hi --
On Sun, 4 Nov 2007, Joshua Muheim wrote: > Hi all > > PHP lets me easily create multidimensional hashes using the following > syntax: > > x = array(); > x["bla"]["some_key"] = true; > > Is Ruby not capable of doing this? > > x = [] > x[:bla][:some_key] = true > > gives me a nil error! Are you sure it's not giving you a "Symbol as array index" error? > What wrong here? A couple of things. You're indexing an array with a symbol, but you have to use an integer. You're also expecting a non-existent array element to be something other than nil, but it isn't: a = [] a[1] # nil You're then trying (or would be, if you used an integer instead of :bla) to call the method [] on nil, and nil has no such method. (Remember that the index syntax, a[n], is actually a method call: a.[](n).) David -- Upcoming training by David A. Black/Ruby Power and Light, LLC: * Advancing With Rails, Edison, NJ, November 6-9 * Advancing With Rails, Berlin, Germany, November 19-22 * Intro to Rails, London, UK, December 3-6 (by Skills Matter) See http://www.rubypal.com for details! |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
Ups I'm sorry, I messed things up because PHP uses [] for both arrays
and hashes. x = array(); x["bla"]["some_key"] = true; Is Ruby not capable of doing this? x = {} x[:bla][:some_key] = true -- Posted via http://www.ruby-forum.com/. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
On 11/3/07, Joshua Muheim <forum@josh.ch> wrote:
> Hi all > > PHP lets me easily create multidimensional hashes using the following > syntax: > > x = array(); > x["bla"]["some_key"] = true; > > Is Ruby not capable of doing this? > > x = [] > x[:bla][:some_key] = true > > gives me a nil error! As David said, if you want to create hashes, you have to create Hashes ;-) Then, you can tell Hash.new what is the default value, so you can create hash, that will contain by default empty hashes: x = Hash.new { Hash.new } This will add an "automatic" two level hash. I don't know quickly how to make this indefinitly deep, you can at least repeat the pattern. Please note that it is not enough to write Hash.new { {} } as the inner will create one particular Hash instance, that all keys will reference. You need Hash.new to create a new hash for each key. Jano |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
Joshua Muheim wrote:
> Ups I'm sorry, I messed things up because PHP uses [] for both arrays > and hashes. > > x = array(); > x["bla"]["some_key"] = true; > > Is Ruby not capable of doing this? > > x = {} > x[:bla][:some_key] = true > For a 2-dimensional Hash you can use: h = Hash.new({}) See the documentation of the initialize method of the Hash class. Here it basically tells the object to use a default value (an empty hash) for uninitialized keys. Lionel |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
Jano Svitok wrote:
> > Please note that it is not enough to write Hash.new { {} } as the > inner will create one particular Hash instance, that all keys will > reference. You need Hash.new to create a new hash for each key. > Ooopps, my bad. Lionel |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
Joshua Muheim wrote:
> PHP lets me easily create multidimensional hashes[...] > Is Ruby not capable of doing this? > > x = [] That's an array. A hash would be x={} > x[:bla][:some_key] = true > > gives me a nil error! The code above should give you a different error. If you use {} it should give you a nil error because x[:bla] would return nil and nil doesn't have a method []. To fix that you have to make x return a hash for non-existant keys. That would work as such: x = Hash.new {|h,k| h[k] = Hash.new} x[:bla][:some_key] = true Or if you want an arbitrary amount of nesting: blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk) x[:la][:li][:lu][:chunky][:bacon][:foo] = "bar" HTH, Sebastian -- Jabber: sepp2k@jabber.org ICQ: 205544826 |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
Lionel Bouton wrote:
> For a 2-dimensional Hash you can use: > > h = Hash.new({}) That won't result in the behaviour most people would expect from a 2-dimensional hash: >> h = Hash.new({}) => {} >> h[:a][:foo] = "bar" => "bar" >> h[:a] => {:foo=>"bar"} >> h => {} >> h[:b][:chunky] = "bacon" => "bacon" >> h[:b] => {:chunky=>"bacon", :foo=>"bar"} >> h[:c] => {:chunky=>"bacon", :foo=>"bar"} >> h => {} You want to a) use the block-form of Hash.new to not use the same instance of a hash everytime and b) assign the new Hash instead of just returning it. -- Jabber: sepp2k@jabber.org ICQ: 205544826 |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
Hi --
On Sun, 4 Nov 2007, Jano Svitok wrote: > On 11/3/07, Joshua Muheim <forum@josh.ch> wrote: >> Hi all >> >> PHP lets me easily create multidimensional hashes using the following >> syntax: >> >> x = array(); >> x["bla"]["some_key"] = true; >> >> Is Ruby not capable of doing this? >> >> x = [] >> x[:bla][:some_key] = true >> >> gives me a nil error! > > As David said, if you want to create hashes, you have to create Hashes ;-) > > Then, you can tell Hash.new what is the default value, so you can > create hash, that will contain by default empty hashes: > > x = Hash.new { Hash.new } > > This will add an "automatic" two level hash. I don't know quickly how > to make this indefinitly deep, you can at least repeat the pattern. > > Please note that it is not enough to write Hash.new { {} } as the > inner will create one particular Hash instance, that all keys will > reference. You need Hash.new to create a new hash for each key. That's not quite right. The only-one-object thing is when you do this: h = Hash.new({}) If you use a block, it gets executed each time -- so in your example, a new hash would get created. However, it's important to remember that what you're setting is the default value (or behavior) for *non-existent* keys. So if you do: h = Hash.new { {} } a = h[1] a is now a hash, but h still has no keys. The block is automatically passed the hash itself, and the key: h = Hash.new {|hash,key| # do stuff with hash and key } and you can use that fact to actually add the key to the hash. David -- Upcoming training by David A. Black/Ruby Power and Light, LLC: * Advancing With Rails, Edison, NJ, November 6-9 * Advancing With Rails, Berlin, Germany, November 19-22 * Intro to Rails, London, UK, December 3-6 (by Skills Matter) See http://www.rubypal.com for details! |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
Joshua Muheim wrote:
> Hi all > > PHP lets me easily create multidimensional hashes using the following > syntax: > > x = array(); > x["bla"]["some_key"] = true; > > Is Ruby not capable of doing this? > > x = [] > x[:bla][:some_key] = true > > gives me a nil error! > x = [] x[:blah][:some_key] = true --output:-- `[]': Symbol as array index (TypeError) from r5test.rb:2 In ruby, array indexes must be non-negative integers-not symbols, not strings, not floats...err this actually 'works': x = [] x[3.5] = true puts x[3.5] --output:-- true But, I think that ruby must convert the float to an int. However, this works: puts x.values_at(3.5) --output:-- true But, then so does this: puts x.values_at(3) --output:-- true So, I can't really figure out a way to prove that the index value can't be a float. Anyway... To create nested hashes you can do this: x = {} x['a'] = 10 p x x[:bla] = {1=>10, 2=>20} p x --output:-- {"a"=>10} {"a"=>10, :bla=>{1=>10, 2=>20}} Writing this is problematic, though: x = {} x[:bla][:some_key] = true --output:-- undefined method `[]=' for nil:NilClass (NoMethodError) That is equivalent to: x = {} lookup1 = x[:bla] lookup1[:some_key] = true and when you lookup a non-existent key for a hash, it returns nil. As a result, lookup1 is assigned nil. But in ruby you can make hashes to return whatever you want when you lookup a non-existent key. You do that by specifying what you want returned in the Hash constructor: x = Hash.new {|hash, key| hash[key] = {} } x[:bla][:some_key] = true p x --output:-- {:bla=>{:some_key=>true}} -- Posted via http://www.ruby-forum.com/. |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
Jano Svitok wrote:
> > x = Hash.new { Hash.new } > > This will add an "automatic" two level hash. I don't know quickly how > to make this indefinitly deep, you can at least repeat the pattern. > > Please note that it is not enough to write Hash.new { {} } as the > inner will create one particular Hash instance, that all keys will > reference. You need Hash.new to create a new hash for each key. > > Jano As far as I can tell neither of the following does anything: x = Hash.new { Hash.new } y = Hash.new{ {} } x[:bla][:some_key] = true y[:bla][:some_key] = true p x, y --output:-- {} {} From the docs for Hash.new: If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block's responsibility to store the value in the hash if required. Your lookup of a non-existent key returns a hash, but the returned hash never gets stored in the original hash, so the original hash remains empty. -- Posted via http://www.ruby-forum.com/. |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
|
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote:
> Joshua Muheim wrote: >> PHP lets me easily create multidimensional hashes[...] Is Ruby not >> capable of doing this? >> >> x = [] > > That's an array. A hash would be x={} > >> x[:bla][:some_key] = true >> >> gives me a nil error! > > The code above should give you a different error. If you use {} it > should give you a nil error because x[:bla] would return nil and nil > doesn't have a method []. > To fix that you have to make x return a hash for non-existant keys. That > would work as such: > x = Hash.new {|h,k| h[k] = Hash.new} > x[:bla][:some_key] = true > > Or if you want an arbitrary amount of nesting: > > blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk) > x[:la][:li][:lu][:chunky][:bacon][:foo] = "bar" > > > HTH, > Sebastian sepp2k's answer is the only correct answer in this thread. --Ken -- Ken Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/ |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote:
> Joshua Muheim wrote: >> PHP lets me easily create multidimensional hashes[...] Is Ruby not >> capable of doing this? >> >> x = [] > > That's an array. A hash would be x={} > >> x[:bla][:some_key] = true >> >> gives me a nil error! > > The code above should give you a different error. If you use {} it > should give you a nil error because x[:bla] would return nil and nil > doesn't have a method []. > To fix that you have to make x return a hash for non-existant keys. That > would work as such: > x = Hash.new {|h,k| h[k] = Hash.new} > x[:bla][:some_key] = true > > Or if you want an arbitrary amount of nesting: > > blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk) > x[:la][:li][:lu][:chunky][:bacon][:foo] = "bar" > > > HTH, > Sebastian Or use Hash#autonew from the facets gem, which does precisely the same thing. --Ken -- Ken Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/ |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
Hi --
On Sun, 4 Nov 2007, Ken Bloom wrote: > sepp2k's answer is the only correct answer in this thread. Could you point out the mistakes in my answers for me? I'd be interested. Thanks -- David -- Upcoming training by David A. Black/Ruby Power and Light, LLC: * Advancing With Rails, Edison, NJ, November 6-9 * Advancing With Rails, Berlin, Germany, November 19-22 * Intro to Rails, London, UK, December 3-6 (by Skills Matter) See http://www.rubypal.com for details! |
|
|
|
#16 |
|
Messages: n/a
Hébergeur: |
On Sat, 03 Nov 2007 20:46:00 -0500, dblack wrote:
> Hi -- > > On Sun, 4 Nov 2007, Ken Bloom wrote: > >> sepp2k's answer is the only correct answer in this thread. > > Could you point out the mistakes in my answers for me? I'd be > interested. Thanks -- > > > David The Hash.new constructor has several different behaviors depending on what you pass it: Hash.new(param) creates a new hash where the object in param is returned whenever a specific key is not found. No new key is created in the hash: a=Hash.new(0) => {} a[:foo] => 0 a => {} Additionally, this same object is constructed once, and used every time you call the hash so: Foo=Struct.new(:bar) => Foo h=Hash.new(Foo.new) => {} h[:baz].bar="Hello" => "Hello" h => {} h[:baz] => #<struct Foo bar="Hello"> h[:furby] => #<struct Foo bar="Hello"> I haven't created any keys, here, but I've changed the default value of the hash. Hash.new{code} creates a new hash where default values are generated by executing the block {code}. When used as Hash.new{param} the code inside the block is called every time, and a new object is constructed every time. (because {} and [] construct a new object every time they are encountered in execution, here they are encountered every time the block is called, whereas before they were encountered only once when the hash was constructed). But the semantics of key creation are the same as before -- no new keys are created until you explicitly assign to the hash. a=Hash.new{0} => {} a[:foo] => 0 a => {} Foo=Struct.new(:bar) => Foo h=Hash.new{Foo.new} => {} h[:baz].bar="Hello" => "Hello" h => {} h[:baz] => #<struct Foo bar=nil> h[:furby] => #<struct Foo bar=nil> Hash.new{|h,k| h[k]=param} this finally assigns to the hash when you access the hash. Foo=Struct.new(:bar) => Foo h=Hash.new{|h,k| h[k]=Foo.new} => {} h[:baz].bar="Hello" => "Hello" h => {:baz=>#<struct Foo bar="Hello">} h[:baz] => #<struct Foo bar="Hello"> h[:furby] => #<struct Foo bar=nil> h => {:baz=>#<struct Foo bar="Hello">, :furby=>#<struct Foo bar=nil>} Personally, I think that Hash might benefit from having a few more named constructors so that people can guess the expected behavior by name. Particularly one that automatically does the whole Hash.new{|h,k| h[k]=param} thing, through judicious use of #dup. class Hash def self.new_add obj Hash.new{|h,k| h[k]=obj.dup} end end -- Ken Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/ |
|
|
|
#17 |
|
Messages: n/a
Hébergeur: |
David A. Black wrote:
> Hi -- > > On Sun, 4 Nov 2007, Ken Bloom wrote: > >> sepp2k's answer is the only correct answer in this thread. > > Could you point out the mistakes in my answers for me? I'd be > interested. Thanks -- > In your first post, you misspelled the op's name: >David A. Black wrote: > Hi -- and then you followed that up with the very same mistake in your second post: >David A. Black wrote: > Hi -- Can you blame the indignation felt by subsequent readers? -- Posted via http://www.ruby-forum.com/. |
|
|
|
#18 |
|
Messages: n/a
Hébergeur: |
Ken Bloom wrote:
> On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote: > >>> gives me a nil error! >> >> blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk) >> x[:la][:li][:lu][:chunky][:bacon][:foo] = "bar" >> >> >> HTH, >> Sebastian > > sepp2k's answer is the only correct answer in this thread. > Darn it! I knew someone would spot the error here: x = Hash.new {|hash, key| hash[key] = {} } x[:bla][:some_key] = true p x --output:-- {:bla=>{:some_key=>true}} But I couldn't figure out how to do it, so I fudged the output. It was really: --output:-- Nuh uh, shorty. Don't bring that stepped on hash round here no mo'. -- Posted via http://www.ruby-forum.com/. |
|
|
|
#19 |
|
Messages: n/a
Hébergeur: |
> x=[]; x[:bla][:some_key] does not work?
> Posted by Joshua Muheim (josh) on 03.11.2007 21:21 > Hi all > > PHP lets me easily create multidimensional hashes using the following > syntax: > > x = array(); > x["bla"]["some_key"] = true; > > Is Ruby not capable of doing this? > > x = [] > x[:bla][:some_key] = true > > gives me a nil error! > > What wrong here? How about this approach: a = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) } a[2][1]=2 a[2][2][3]=4 a[3][1][1][1]=1 p a #=> {2=>{1=>2, 2=>{3=>4}}, 3=>{1=>{1=>{1=>1}}}} ( http://snippets.dzone.com/posts/show/4146 ) Cheers, j. k. -- Posted via http://www.ruby-forum.com/. |
|
|
|
#20 |
|
Messages: n/a
Hébergeur: |
Hi --
On Sun, 4 Nov 2007, Ken Bloom wrote: > On Sat, 03 Nov 2007 20:46:00 -0500, dblack wrote: > >> Hi -- >> >> On Sun, 4 Nov 2007, Ken Bloom wrote: >> >>> sepp2k's answer is the only correct answer in this thread. >> >> Could you point out the mistakes in my answers for me? I'd be >> interested. Thanks -- >> >> >> David > > The Hash.new constructor has several different behaviors depending on > what you pass it: I didn't say it didn't. I'm still wondering what you found in my posts that was incorrect, if you wouldn't mind sharing. David -- Upcoming training by David A. Black/Ruby Power and Light, LLC: * Advancing With Rails, Edison, NJ, November 6-9 * Advancing With Rails, Berlin, Germany, November 19-22 * Intro to Rails, London, UK, December 3-6 (by Skills Matter) See http://www.rubypal.com for details! |
|
|
|
#21 |
|
Messages: n/a
Hébergeur: |
On Sun, 04 Nov 2007 06:38:52 -0500, dblack wrote:
> Hi -- > > On Sun, 4 Nov 2007, Ken Bloom wrote: > >> On Sat, 03 Nov 2007 20:46:00 -0500, dblack wrote: >> >>> Hi -- >>> >>> On Sun, 4 Nov 2007, Ken Bloom wrote: >>> >>>> sepp2k's answer is the only correct answer in this thread. >>> >>> Could you point out the mistakes in my answers for me? I'd be >>> interested. Thanks -- >>> >>> >>> David >> >> The Hash.new constructor has several different behaviors depending on >> what you pass it: > > I didn't say it didn't. I'm still wondering what you found in my posts > that was incorrect, if you wouldn't mind sharing. > > > David OK. I see you've got the semantics right, but you didn't point out the solution. --Ken -- Ken Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/ |
|
|
|
#22 |
|
Messages: n/a
Hébergeur: |
> OK. I see you've got the semantics right, but you didn't point out the
> solution. In other words, nothing he said was wrong, but he still should have said something different? Think about it. When you made this post: > sepp2k's answer is the only correct answer in this thread. ...you doomed this whole thread to bickering. Don't be doing that. Play it cool, and you get to have much better conversations. Otherwise this place just turns into programming.reddit.com, or the Rails list, or something like that. -- Giles Bowkett Blog: http://gilesbowkett.blogspot.com Portfolio: http://www.gilesgoatboy.org Tumblelog: http://giles.tumblr.com/ |
|
|
|
#23 |
|
Messages: n/a
Hébergeur: |
On 3-Nov-07, at 4:44 PM, Sebastian Hungerecker wrote: > > blk = lambda {|h,k| h[k] = Hash.new(&blk)} > x = Hash.new(&blk) > x[:la][:li][:lu][:chunky][:bacon][:foo] = "bar" > > On 4-Nov-07, at 4:34 AM, Jimmy Kofler wrote: > a = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) } Very nice, Sebastian and Jimmy. Leading to the obvious... class NestedHash < Hash def initialize blk = lambda {|h,k| h[k] = NestedHash.new(&blk)} super(&blk) end end class Hash def Hash.new_nested_hash Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) } end end ... and with no comment on propriety. I've been using Ruby for years and I'm still startled by what you can do when passing blocks to initializers. A few weeks ago I stumbled on this: Hash.new{ | h, k | h[k] = Array.new } which is just a hash of arrays, completely trivial really, but I'd never thought of it before, and now I'm using it all over the place. And I was really impressed with Ruby when I discovered using blocks with gsub. Cheers, Bob ---- Bob Hutchison -- tumblelog at http://www.recursive.ca/so/ Recursive Design Inc. -- weblog at http://www.recursive.ca/hutch http://www.recursive.ca/ -- works on http://www.raconteur.info/cms-for-static-content/home/ |
|
|
|
#24 |
|
Messages: n/a
Hébergeur: |
Hi --
On Mon, 5 Nov 2007, Bob Hutchison wrote: > I've been using Ruby for years and I'm still startled by what you can do when > passing blocks to initializers. A few weeks ago I stumbled on this: > > Hash.new{ | h, k | h[k] = Array.new } > > which is just a hash of arrays, completely trivial really, but I'd never > thought of it before, and now I'm using it all over the place. My favorite is: class NonexistentKeyError < StandardError end hash = Hash.new {|h,k| raise NonexistentKeyError, "No such key: #{k}" } :-) David -- Upcoming training by David A. Black/Ruby Power and Light, LLC: * Advancing With Rails, Edison, NJ, November 6-9 * Advancing With Rails, Berlin, Germany, November 19-22 * Intro to Rails, London, UK, December 3-6 (by Skills Matter) See http://www.rubypal.com for details! |
|
|
|
#25 |
|
Messages: n/a
Hébergeur: |
On Nov 5, 2007, at 5:27 AM, David A. Black wrote: > My favorite is: > > class NonexistentKeyError < StandardError > end > > hash = Hash.new {|h,k| raise NonexistentKeyError, "No such key: # > {k}" } An alternate solution: a = Hash.new { |h,k| h.fetch k } David's solution is nice because the message includes the key. Maybe Ruby's IndexError message should report the unknown key. Gary Wright |
|
![]() |
| Outils de la discussion | |
|
|