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

Ashley Moran 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:

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.

···

On 6/22/06, Ashley Moran <work@ashleymoran.me.uk> wrote:

data_items = DataItem.find(:all)
# data_items[3].identifier => "my_identifier"
data_items[:my_identifier].do_something # => data_items[3].do_something

--
Lou.

Ashley Moran wrote:

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:

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

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

Ashley

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

cheers

Simon

$ 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>

···

On 6/22/06, Ashley Moran <work@ashleymoran.me.uk> wrote:

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

--
- Simen

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

···

On 6/22/06, Simen Edvardsen <toalett@gmail.com> wrote:

$ 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>

On 6/22/06, Ashley Moran <work@ashleymoran.me.uk> wrote:
> 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
>

--
- Simen

--
- Simen

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:

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

I like!

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.

···

On Jun 22, 2006, at 8:25 pm, Simen Edvardsen wrote:
On Jun 22, 2006, at 8:09 pm, Simon Kröger wrote:
On Jun 22, 2006, at 8:05 pm, Louis J Scoras 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. :-))

> 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

···

2006/6/22, Ashley Moran <work@ashleymoran.me.uk>:

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

--
Have a look: Robert K. | Flickr