Turning an Array into a Hash based on an attribute value

A guy I work with has some some work on an ActiveRecord class that
basically reads static lookup data, and contains (among other things)
an “identifier” attribute. Given an array of all these objects he
wanted to access them by the contents of the identifier attribute, eg:

data_items = DataItem.find(:all)

data_items[3].identifier => “my_identifier”

data_items[:my_identifier].do_something # => data_items[3].do_something

The best I can come up with is this:

class Array
def hash_on(method)
method = method.to_sym
hash = { }
self.each do |i|
hash[i.send(method).to_s.to_sym] = i
end
hash
end
end

[1, “2”, :three].hash_on(:class)
=> {:String=>“2”, :Symbol=>:three, :Fixnum=>1}

Is there a neater (one-line?) way I missed?

Ashley

On 6/22/06, Ashley M. [email protected] wrote:

data_items = DataItem.find(:all)

data_items[3].identifier => “my_identifier”

data_items[:my_identifier].do_something # => data_items[3].do_something

I think your implementation of this looks pretty reasonable, but the
above begs the question, why does he want to do this? One would think
that you’d be better off just asking ActiveRecord for the correct
object, no?

I’m not sure of the api–i.e.: this is untested–but why not something
like this:

particular_item = DataItem.find_by_identifer(“my_identifier”)
particular_item.do_something

This is the sort of thing that databases are good at.

Ashley M. wrote:

Is there a neater (one-line?) way I missed?

How about inject:

[1, “2”, :three].inject({}) { |h, e| h[e.class] = e; h }

:slight_smile:

$ irb
irb(main):001:0> class Array
irb(main):002:1> def hash_on(method)
irb(main):003:2>
Hash[*self.map{|m|m.send(method).to_s.to_sym}.zip(self).flatten ]
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> [1, “2”, :three].hash_on(:class)
=> {:String=>“2”, :Symbol=>:three, :Fixnum=>1}
irb(main):007:0>

Ashley M. wrote:

Ashley
class Array
def hash_on(method)
inject({}){|h, i| h.merge({i.send(method).to_s.to_sym, i})}
end
end

cheers

Simon

Disclaimer: inject is probably more appropriate, but my solution at
least looks cooler :wink:

On Jun 22, 2006, at 8:25 pm, Simen E. wrote:

Hash[*self.map{|m|m.send(method).to_s.to_sym}.zip(self).flatten ]

Disclaimer: inject is probably more appropriate, but my solution at
least looks cooler :wink:

Simen… you don’t come from a Perl background do you :wink:

On Jun 22, 2006, at 8:09 pm, Simon Kröger wrote:

class Array
def hash_on(method)
inject({}){|h, i| h.merge({i.send(method).to_s.to_sym, i})}
end
end

I like!

On Jun 22, 2006, at 8:05 pm, Louis J Scoras wrote:

I’m not sure of the api–i.e.: this is untested–but why not something
like this:

particular_item = DataItem.find_by_identifer(“my_identifier”)
particular_item.do_something

This is the sort of thing that databases are good at.

It avoids hitting the database every time - if there are 500 items in
the list I’d rather get them all out at once than hit the server 500
times.

Thanks for the replies people.

Incidentally… what is the convention here on CCing posters in
replies? I come from the FreeBSD lists where it’s normal to CC the
poster so they don’t have to check the list. But people on Ruby
lists are really surprised when I do that.

2006/6/22, Ashley M. [email protected]:

On Jun 22, 2006, at 8:25 pm, Simen E. wrote:

Hash[*self.map{|m|m.send(method).to_s.to_sym}.zip(self).flatten ]

Disclaimer: inject is probably more appropriate, but my solution at
least looks cooler :wink:

Simen… you don’t come from a Perl background do you :wink:

LOL - Could also be awk - because the code looks so awkward. :-))

On Jun 22, 2006, at 8:09 pm, Simon Kröger wrote:

class Array
def hash_on(method)
inject({}){|h, i| h.merge({i.send(method).to_s.to_sym, i})}
end
end

I like!

I like Robin’s inject solution more - it’s more efficient and avoids
all those short lived hash objects. Also, I’d put it into Enumerable,
add a block and provide a list of args instead of the method name
only:

module Enumerable
def to_hash(*args,&b)
inject({}) {|h,e| h[b ? b[e] : e.send(*args)] = e; h}
end
end

irb(main):019:0> %w{foo bar baz}.to_hash {|x| x[0]}
=> {102=>“foo”, 98=>“baz”}
irb(main):020:0> %w{foo bar baz}.to_hash(:[], 0)
=> {102=>“foo”, 98=>“baz”}

silly example to demonstrate what you cannot do without a block

irb(main):022:0> %w{foo bar baz}.to_hash {|x| x.length * rand(100)}
=> {132=>“bar”, 15=>“foo”, 186=>“baz”}

Incidentally… what is the convention here on CCing posters in
replies? I come from the FreeBSD lists where it’s normal to CC the
poster so they don’t have to check the list. But people on Ruby
lists are really surprised when I do that.

I usually don’t because I assume people are checking thelist anyway -
and until now nobody has complaint that he / she missed a posting from
me. :slight_smile:

Kind regards

robert