From: 7stud -- on
7stud -- wrote:
> 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...when you try to access non-existent keys.
--
Posted via http://www.ruby-forum.com/.

From: Glenn Jackman on
At 2009-09-10 09:16PM, "7stud --" wrote:
> 7stud -- wrote:
> > 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...when you try to access non-existent keys.

While I thank you for takking the time, I can't say I'm enlightened by
your explanation. What's the real difference between the blocks

bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h}
baz = products.inject(Hash.new([])) {|h,p| h[p.category] += [p]; h}

Is it because += explicitly assigns a new object to the hash key?

--
Glenn Jackman
Write a wise saying and your name will live forever. -- Anonymous
From: David A. Black on
Hi --

On Fri, 11 Sep 2009, Glenn Jackman wrote:

> At 2009-09-10 09:16PM, "7stud --" wrote:
>> 7stud -- wrote:
>>> 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...when you try to access non-existent keys.
>
> While I thank you for takking the time, I can't say I'm enlightened by
> your explanation. What's the real difference between the blocks
>
> bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h}
> baz = products.inject(Hash.new([])) {|h,p| h[p.category] += [p]; h}
>
> Is it because += explicitly assigns a new object to the hash key?

Yes. Here's another way to look at it:

array = []
hash = Hash.new(array)
hash[:x] << 1 # equivalent to: array << 1
hash[:y] << 2 # equivalent to: array << 2
hash[:z] += [:a,:b,:c] # equivalent to: h[:z] = array + [:a,:b,:c]


David

--
David A. Black / Ruby Power and Light, LLC / http://www.rubypal.com
Ruby/Rails training, mentoring, consulting, code-review
Latest book: The Well-Grounded Rubyist (http://www.manning.com/black2)

September Ruby training in NJ has been POSTPONED. Details to follow.

From: Rick DeNatale on
On Thu, Sep 10, 2009 at 8:42 PM, 7stud -- <bbxx789_05ss(a)yahoo.com> wrote:
> 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.


The thread has moved on a bit, but.

He actually isn't USING the block form of Hash.new, the only blocks
are arguments to inject.

And Hash.new {|h,k| ... } is what I tend to reach for in a situation like this.

foo = products.inject(Hash.new {|h,k| h[k] = []}) {|h,p|
h[p.category] << p; h}


--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

From: 7stud -- on
Glenn Jackman wrote:
> At 2009-09-10 09:16PM, "7stud --" wrote:
>> > p h
>> >
>> > {"A"=>[10, 20], "B"=>[10, 20]}
>> >
>>
>> Yep, ruby hands you a reference to the same array over and over
>> again...when you try to access non-existent keys.
>
> While I thank you for takking the time, I can't say I'm enlightened by
> your explanation. What's the real difference between the blocks
>
> bar = products.inject(Hash.new([])) {|h,p| h[p.category] << p; h}
> baz = products.inject(Hash.new([])) {|h,p| h[p.category] += [p]; h}
>
> Is it because += explicitly assigns a new object to the hash key?

Yes. When the key doesn't exist the first line is equivalent to:

bar = products.inject(Hash.new([])) {|h,p| [] << p; h}

which does nothing to the hash--all it does is append p to an empty
array, and then the empty array is discarded.

The second line is equivalent to:

baz = products.inject(Hash.new([])) {|h,p| h[p.category] = h[p.category]
+ [p]; h}

If you access a non-existent key, say h["A"], then that line is
equivalent to

baz = products.inject(Hash.new([])) {|h,p| h["A"] = [] + [p]; h}

or

baz = products.inject(Hash.new([])) {|h,p| h["A"] = [p]; h}

which explicitly sets a key in the hash to the value [p], thereby
altering the hash.
--
Posted via http://www.ruby-forum.com/.