From: Glenn Jackman on 10 Sep 2009 19:28 I was looking at this problem on Stack Overflow (this one: http://stackoverflow.com/questions/1405657/reorganizing-ruby-array-into-hash The question is how to "convert" an array of objects into a hash. Consider this code: require 'pp' p RUBY_VERSION Product = Struct.new(:name, :category) products = [ ['Apple','Golden Delicious'], ['Apple','Granny Smith'], ['Orange','Navel'] ].collect {|cat, name| Product.new(name, cat)} foo = products.inject({}) {|h,p| h[p.category] ||= []; h[p.category] << p; h} pp foo bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h} pp bar baz = products.inject(Hash.new([])) {|h,p| h[p.category] += p; h} pp baz It outputs: "1.8.7" {"Orange"=>[#<struct Product name="Navel", category="Orange">], "Apple"=> [#<struct Product name="Golden Delicious", category="Apple">, #<struct Product name="Granny Smith", category="Apple">]} {} {"Orange"=>[#<struct Product name="Navel", category="Orange">], "Apple"=> [#<struct Product name="Golden Delicious", category="Apple">, #<struct Product name="Granny Smith", category="Apple">]} My question: why is bar empty? -- Glenn Jackman Write a wise saying and your name will live forever. -- Anonymous
From: Ehsanul Hoque on 10 Sep 2009 20:07 I don't have an answer, but trying out the code in JRuby, I can see that the default value for bar becomes simply the products array after the inject() method is used. One more thing that requires an explanation. You can see this by appending this line to your code: pp bar["random string here"] > Date: Fri, 11 Sep 2009 08:30:07 +0900 > From: glennj(a)ncf.ca > Subject: populating a hash from an array using inject > To: ruby-talk(a)ruby-lang.org > > I was looking at this problem on Stack Overflow (this one: > http://stackoverflow.com/questions/1405657/reorganizing-ruby-array-into-hash > > The question is how to "convert" an array of objects into a hash. > > Consider this code: > > require 'pp' > p RUBY_VERSION > > Product = Struct.new(:name, :category) > products = [ > ['Apple','Golden Delicious'], > ['Apple','Granny Smith'], > ['Orange','Navel'] > ].collect {|cat, name| Product.new(name, cat)} > > foo = products.inject({}) {|h,p| h[p.category] ||= []; h[p.category] << p; h} > pp foo > > bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h} > pp bar > > baz = products.inject(Hash.new([])) {|h,p| h[p.category] += p; h} > pp baz > > It outputs: > > "1.8.7" > {"Orange"=>[#<struct Product name="Navel", category="Orange">], > "Apple"=> > [#<struct Product name="Golden Delicious", category="Apple">, > #<struct Product name="Granny Smith", category="Apple">]} > {} > {"Orange"=>[#<struct Product name="Navel", category="Orange">], > "Apple"=> > [#<struct Product name="Golden Delicious", category="Apple">, > #<struct Product name="Granny Smith", category="Apple">]} > > My question: why is bar empty? > > -- > Glenn Jackman > Write a wise saying and your name will live forever. -- Anonymous > _________________________________________________________________ Windows Live: Make it easier for your friends to see what youre up to on Facebook. http://windowslive.com/Campaign/SocialNetworking?ocid=PID23285::T:WLMTAGL:ON:WL:en-US:SI_SB_facebook:082009
From: 7stud -- on 10 Sep 2009 20:42 Glenn Jackman wrote: > I was looking at this problem on Stack Overflow (this one: > http://stackoverflow.com/questions/1405657/reorganizing-ruby-array-into-hash > > The question is how to "convert" an array of objects into a hash. > > Consider this code: > > require 'pp' > p RUBY_VERSION > > Product = Struct.new(:name, :category) > products = [ > ['Apple','Golden Delicious'], > ['Apple','Granny Smith'], > ['Orange','Navel'] > ].collect {|cat, name| Product.new(name, cat)} > > foo = products.inject({}) {|h,p| h[p.category] ||= []; h[p.category] > << p; h} > pp foo > > bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h} > pp bar > > baz = products.inject(Hash.new([])) {|h,p| h[p.category] += p; h} > pp baz > > It outputs: > > "1.8.7" > {"Orange"=>[#<struct Product name="Navel", category="Orange">], > "Apple"=> Even though it is not very well written, if you read the documentation on creating hashes with default values: $ ri Hash.new -------------------------------------------------------------- Hash::new Hash.new => hash Hash.new(obj) => aHash Hash.new {|hash, key| block } => aHash ------------------------------------------------------------------------ Returns a new, empty hash. If this hash is subsequently accessed by a key that doesn't correspond to a hash entry, the value returned depends on the style of +new+ used to create the hash. In the first form, the access returns +nil+. If _obj_ is specified, this single object will be used for all _default values_. 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. h = Hash.new("Go Fish") h["a"] = 100 h["b"] = 200 h["a"] #=> 100 h["c"] #=> "Go Fish" # The following alters the single default object h["c"].upcase! #=> "GO FISH" h["d"] #=> "GO FISH" h.keys #=> ["a", "b"] ----------------------------------------------------------- The key line is: ***8It is the block's responsibility to store the value in the hash if required.*** Because your block does not assign the array to a hash key, the array is discarded. -- Posted via http://www.ruby-forum.com/.
From: 7stud -- on 10 Sep 2009 20:48 Sorry, I pasted the ri output on top of the first part of my post, which said something to the effect of: > My question: why is bar empty? ...because h[non_existent_key] sends an array that is unassociated with any hash to the block. In other words, creating a hash with a default value does not cause a key/value pair to be created in the hash when a non-existent key is accessed. -- Posted via http://www.ruby-forum.com/.
From: 7stud -- on 10 Sep 2009 21:07 7stud -- wrote: > Sorry, I pasted the ri output on top of the first part of my post, which > said something to the effect of: > >> My question: why is bar empty? > > ...because h[non_existent_key] sends an array that is unassociated with > any hash to the block. In other words, creating a hash with a default > value does not cause a key/value pair to be created in the hash when a > non-existent key is accessed. It was probably best that first sentence was erased. Here is what I was trying to say: h = Hash.new([]) result = h["A"] p result --output:-- [] p h --output:-- {} When you write: h[p.category] << p The example above demonstrates that is equivalent* to: [] << p ...and that simply appends an object to an empty array, and does nothing to the hash. And you are in even worse shape than you realize. If you take the empty array that is returned as the default value and append an object to the array, and then manually assign the array to a key in the hash: h = Hash.new([]) h["A"] = h["A"] << 10 #==>[10] h["B"] = h["B"] << 20 #==>[20] Look what happens: p h {"A"=>[10, 20], "B"=>[10, 20]} Yep, ruby hands you a reference to the same array over and over again. -- Posted via http://www.ruby-forum.com/.
|
Next
|
Last
Pages: 1 2 3 Prev: iconv "\n" (Iconv::InvalidCharacter) Next: [QUIZ] URL Shenanigans (#220) |