tags:

views:

83

answers:

3

I have a simple array:

arr = ["apples", "bananas", "coconuts", "watermelons"]

I also have a function f that will perform an operation on a single string input and return a value. This operation is very expensive, so I would like to memoize the results in the hash.

I know I can make the desired hash with something like this:

h = {}
arr.each { |a| h[a] = f(a) }

What I'd like to do is not have to initialize h, so that I can just write something like this:

h = arr.(???) { |a| a => f(a) }

Can that be done?

A: 

http://snippets.dzone.com/posts/show/302

Evolve
Two things: first, rather than simply posting a link, please post code and any relevant explanation here (that's the point of this site); second, and this is really for the OP, why turn something clear and brief into something cryptic and compressed just to save writing `h = {}`?
Telemachus
Telemachus: Curiosity. You can't gain a deeper understanding of anything if you don't ask questions you think are interesting once in a while. (Or, at least, that's how it's always been for me.)
Wizzlewott
Sorry have been busy working, thought dropping a quick link to an unanswered question is better than leaving it unanswered. Feel free to post your own answer. The best answers get the points. That's how it works. When I post questions, Im grateful for all responses I get.
Evolve
+2  A: 

Say you have a function with a funtastic name: "f"

def f(fruit)
   fruit + "!"
end

arr = ["apples", "bananas", "coconuts", "watermelons"]
h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]

will give you:

{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}
microspino
I think you meant `... { |v| [v, f(v)] }`, but this did the trick!
Wizzlewott
Just one thing - why's there a `*` next to `*arr.collect`?
Jeriko
@Jeriko - the splat operator `*` collects a list into an array or unwinds an array into a list, depending on context. Here it unwinds the array into a list (to be used as the items for the new hash).
Telemachus
So `Hash[]` takes a list (with an even number of items) in terms of `key,value,key,value...`?
Jeriko
@Jeriko Yes - if you give `Hash[]` an odd number of arguments, it blows up. See here for more on the splat operator (which is really very useful in a number of contexts): http://4loc.wordpress.com/2009/01/16/the-splat-operator-in-ruby/
Telemachus
`ArgumentError: odd number of arguments for Hash` :) Thanks
Jeriko
After looking at Jörg's answer and thinking this over some more, note that you can remove both `*` and `flatten` for a simpler version: `h = Hash[ arr.collect { |v| [ v, f(v) ] } ]`. I'm not sure if there's a gotcha I'm not seeing, however.
Telemachus
In Ruby 1.8.7, the ugly `Hash[*key_pairs.flatten]` is simply `Hash[key_pairs]`. Much nicer, and `require 'backports'` if you haven't updated from 1.8.6 yet.
Marc-André Lafortune
+1  A: 

This is what I would probably write:

h = Hash[arr.zip(arr.map(&method(:f)))]

Simple, clear, obvious, declarative. What more could you want?

Jörg W Mittag
I like `zip` as much as the next guy, but since we're already calling `map`, why not leave it at this? `h = Hash[ arr.map { |v| [ v, f(v) ] } ]` Is there an advantage to your version that I'm not seeing?
Telemachus
@Telemachus: With all the Haskell code I've been reading, I just got used to point-free programming, that's all.
Jörg W Mittag