Idiom for Hash like map

class Fruit
attr_accessor :name, :price
def initialize(args)
@name = args[:name]
@price = args[:price]
end
end

array = [ Fruit.new(:name => “apple”, :price => 2),
Fruit.new(:name => “grape”, :price => 20),
Fruit.new(:name => “pear”, :price => 200) ]

hash = Hash.new

array.each do |obj|
hash.merge!({obj.name => obj.price})
end

hash.inspect # => “{“grape”=>20, “apple”=>2, “pear”=>200}”

The “hash = {} / hash.merge!” thing bugs me… what is the better way?

Mikel

Hi –

On Wed, 7 May 2008, Mikel L. wrote:

     Fruit.new(:name => "pear",  :price => 200) ]

The “hash = {} / hash.merge!” thing bugs me… what is the better way?

You might like this better:

hash = Hash[*array.map {|e| [e.name, e.price] }.flatten ]

or this:

hash = array.inject({}) {|h, fruit| h[fruit.name] = fruit.price; h }

David

On May 7, 2008, at 7:18 AM, Mikel L. wrote:

     Fruit.new(:name => "pear",  :price => 200) ]

The “hash = {} / hash.merge!” thing bugs me… what is the better way?

Mikel

If your general question is something like, “How can I take an array
of objects and turn it into a key/value hash where the key and the
value are based on methods on each object?”

Then you could do something like:

irb> Hash[*array.map{|f|[f.name,f.price]}.flatten]
=> {“grape”=>20, “apple”=>2, “pear”=>200}

If any of the attributes (methods) return Arrays, the flatten will not
produce the result that you want. Also, if the 'name’s aren’t unique,
the hash will have fewer pairs that the array had entries.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On May 7, 2008, at 5:18 AM, Mikel L. wrote:

     Fruit.new(:name => "pear",  :price => 200) ]

The “hash = {} / hash.merge!” thing bugs me… what is the better way?

class Fruit
def to_hash
{ :name => name, :price => price }
end
end

array.each{|fruit| hash.update fruit.to_hash}

a @ http://codeforpeople.com/

On Thu, May 8, 2008 at 12:51 AM, ara.t.howard [email protected]
wrote:

On May 7, 2008, at 5:18 AM, Mikel L. wrote:
class Fruit
def to_hash
{ :name => name, :price => price }
end
end
array.each{|fruit| hash.update fruit.to_hash}

Hmm… put the responsibility back into the class. I guess that
actually makes the most sense.

Thanks David and Rob for your suggestions, totally hadn’t considered
those idioms.

Mikel

2008/5/8 Mikel L. [email protected]:

actually makes the most sense.
But this approach has the drawback to create a lot of temporary
instances that are thrown away immediately.

Of course I do not know your use case but if I was building an index
I’d rather insert the object as value instead of a field. Just for
the fun of it:

irb(main):001:0> Fruit = Struct.new :name, :price
=> Fruit
irb(main):002:0> fruits = [Fruit.new(“foo”,1),Fruit.new(“bar”,2)]
=> [#<struct Fruit name=“foo”, price=1>, #<struct Fruit name=“bar”,
price=2>]
irb(main):005:0> indexes = Hash.new {|h,k| h[k]=
Hash.new(&h.default_proc)}
=> {}
irb(main):006:0> fruits.each {|f| f.members.each {|m|
indexes[m][f[m]]=f}}
=> [#<struct Fruit name=“foo”, price=1>, #<struct Fruit name=“bar”,
price=2>]
irb(main):008:0> require ‘pp’
=> true
irb(main):009:0> pp indexes
{“name”=>
{“foo”=>#<struct Fruit name=“foo”, price=1>,
“bar”=>#<struct Fruit name=“bar”, price=2>},
“price”=>
{1=>#<struct Fruit name=“foo”, price=1>,
2=>#<struct Fruit name=“bar”, price=2>}}
=> nil

Cheers

robert