-
-
Save kotp/7628cf7a4745c9179c66 to your computer and use it in GitHub Desktop.
=begin | |
Method returns hash of derivitave values as provided. | |
Example usage: | |
doctest: Setup | |
>> require './build_hash_with_derived_values' | |
=> true | |
doctest: Yardstick test derived result | |
>> yardstick = build_hash_with_derivative_values [:yard, :foot, :inch], | |
[ 1, 3, 12 ] | |
=> {:inch=>36, :foot=>3, :yard=>1} | |
doctest: How many inches in 1.5 feet? | |
We can remember how to do this if we remember that inches in something means | |
to divide that something by inches. Then 1.5 of that something (where "of" | |
means to multiply). | |
>> yardstick[:inch] / yardstick[:foot] * 1.5 | |
=> 18.0 | |
doctest: Units that make up a yard | |
>> yardstick.keys | |
=> [:yard, :foot, :inch] | |
doctest: Units in Year derived result | |
>> Units_In_One_Year = | |
build_hash_with_derivative_values [:year, :day, :hour, :minute, :second], | |
[ 1, 365, 24, 60, 60] | |
=> {:second=>31536000, :minute=>525600, :hour=>8760, :day=>365, :year=>1} | |
=end |
def build_hash_with_derivative_values units, property | |
decrement = 0 ; value = nil # house keeping for the hash assignment | |
hash = {} | |
hash = units.inject do |key| | |
(units.size - decrement ).times do |i| | |
value = 1 unless value # This is opposite of the x ||= value idiom | |
value = property[i] * value | |
hash[units[decrement]] = value | |
decrement += 1 # control the map of the supplied value array. | |
end | |
value = nil | |
hash # this is returned internally to inject. | |
end | |
# hash as created by the inect method is returned. | |
end |
citizen428
commented
Jul 2, 2010
•
Just a slight variation of the above, that saves you from always having to pass in 1 as the first value of property:
# This one fails the test, output below in comments doesn't match the doctest
def build_hash_with_derivative_values(units, property)
properties = property.inject([1]) { |a,v| a << v*a[-1] }
units.zip(properties).inject({}) { |h, (k,v)| h[k] = v ; h }
end
build_hash_with_derivative_values([:yard, :foot, :inch], [3, 12]) # this complicates the API... IMHO
# => {:yard=>1, :foot=>3, :inch=>36}
build_hash_with_derivative_values([:year, :day, :hour, :minute, :second], [365, 24, 60, 60])
# => {:year=>1, :day=>365, :hour=>8760, :minute=>525600, :second=>31536000}
Confirmed Doctest Output
#
Thinking about it, maybe :yard and :year shouldn't be part of the hash at all:
def build_hash_with_derivative_values(units, property)
properties = property.inject([1]) { |a,v| a << v*a[-1] }
units.zip(properties[1..-1]).inject({}) { |h, (k,v)| h[k] = v ; h }
end
yard = build_hash_with_derivative_values([:foot, :inch], [3, 12])
# => {:foot=>3, :inch=>36}
year = build_hash_with_derivative_values([:day, :hour, :minute, :second], [365, 24, 60, 60])
# => {:day=>365, :hour=>8760, :minute=>525600, :second=>31536000}
yard[:inch] # => 36
year[:second] # => 31536000
Yeah, I thought about the not having the base be included. I am still kind of at a loss as to whether that is a good idea.
My thought is on the possibility of expanding the set. For example:
time_units = build_hash_with_derivative_values([:day, :hour, :minute, :second], [365, 24, 60, 60])
and then expand time units to include :millenium, :century, :decade, :year
I see :-) Do you like the solution though?
Oh, do I like it? No... I love that solution!
It is definitely readable, (as soon as you get a handle on what inject is doing)
Wow, interesting idea, Vic. Awesome solution, Michael.
I was inspired. How about this one? Just for fun, though. :)
class Fixnum
def self.build_hash_with_derivative_values(units, property)
properties = (property.inject([1]) { |a,v| a << v*a[-1] })[1..-1]
units.zip(properties).inject({}) { |h, (k,v)| h[k] = v ; h }
end
@@h = build_hash_with_derivative_values [:year, :day, :hour, :minute, :second], [1, 365, 24, 60, 60]
def method_missing m
from, to = m.to_s.split('_to_')
1.0 * self * @@h[to.to_sym] / @@h[from.to_sym]
end
end
puts 3.year_to_day #=> 1095.0
puts 3.hour_to_second #=> 10800.0
puts 10.minute_to_hour #=> 0.166666666666667
That is pretty cool...
Jerry Anning had realized that my original code above (specifically version https://gist.github.com/7628cf7a4745c9179c66/4e40a35942c60d97bdf3fb1259ad7a82fd31dab8 ) passes the test if I comment the inject line, and the appropriate 'end'... which means that I basically built something that does both lines:
properties = property.inject([1]) { |a,v| a << v*a[-1] }
units.zip(properties).inject({}) { |h, (k,v)| h[k] = v ; h }
Victor: I didn't think your original solution was bad, it's readable and does what it's supposed to do. My solution was more a for fun approach and because you asked me on IRC re inject. Given that my main interest nowadays is functional programming, I tend to use the likes of inject, map, zip, etc. a lot. I even have a project here on GH that's supposed to one day become an ebook on functional programming in Ruby, alas I never have enough time for all my different projects...
Thank you... The reason I asked for inject, is because it is way over due that I actually understand how inject works, past the trivial (though mystical) (1..5).inject(:+) invocation, or the written out (1..5).inject { |sum, n| sum + n }
I thank you tons for your solution, because it gave me something more complex to look at, while realizing that my code did the same thing, and gave me more area to understand in the novelty of that small space, and something to compare the behavior and activity with.
I really didn't take it as an opinion on my original solution. I just knew that it 'looked' like it was doing something that could be done differently.
I actually have about 4 different solutions, all that pass the doctests and it will be interesting to see which is more efficient in terms of benchmarks, and profiler measurements. I wish I programmed every day... actually with people, because it is times like these that I learn the most. I only have certain viewpoints, and I stretch them, but it helps to have other eyes on the matter too.
It is why I like working with you guys.
Funny that you bring up (1..5).inject(&:+), since it always struck me as a particularly bad example, since summing the numbers from 1 to n is most efficiently done as (n*(n+1))/2 (http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/runsums/triNbProof.html). A couple of days ago I actually benchmarked it because I was thinking about a blog post called "Why knowing your math helps" and the second solution was considerably faster than inject.
BTW: the written out form of your inject would just return 5, what you meant is (1..5).inject { |sum, n| sum + n }
Does math always win out over inject/iterations? I can't think of a situation where it wouldn't, but then I am not a math nerd.
Also, happy holidays to wherever both are!