Ruby Hash#merge usage in a Rails project

I've been working on a project with a financial side, every component had a price setup in two currencies. First -was the base currency, and the other was the exchanged one (given with an exchanger rate by the user.

So the user creates several components with different base currencies and different exchange currencies. Now the task is to create a monthly, yearly, or whatever the user wishes financial statement which shows the sum of those amounts.

The way I did this was first to collect the currencies and amounts to a hash and then to merge those hashes for the final result.

In ruby this is easy, all the components belonged to a venture, and all of them had the base and exchange currency given.

          def service_cost
            cost, currency, s_cost = {}, '', 0
           
            components.collect do |c|
              currency = c.currency.symbol
              s_cost = c.cost.round
              cost[currency] = cost.key?(currency) ? cost[currency]+ s_cost : s_cost
            end
          
            cost
          end
          

The above code worked as is should in a venture model, I collected all the components and the prices, summed them up and put in a hash. If the currency didn't exist as a key in the hash it was just given the s_cost which is the price of the component. If an the other hand the currency existed in the hash component price was added to the current amount.

All the code above worked as it should I got a hash on then which I could do the merge with a block, to make a financial statement that summed up all the costs for a given time period.

          cost.merge!(another_cost) { |key,old,new| old + new }
          

The above operation merged two hashes with cost in the form of {'$'=>300} and returned a hash in the same form but with added sums for each currency. So foe example if there was {'$'=>300,'other'=>100} and {'$'=>100} it would return {$'=>400,'other'=>100}.

          irb> h1, h2= {'$'=>100}, {'$'=>300,'other'=>100}
          
          irb> h1.merge(h2) {|key,old,new| new+old}
          
          => {'$'=>400,'other'=>100}
          

There is a problem though adding works as it should but if you need to make a subtraction from the values? This gives us the wrong values. In adding Ruby 'assumes' that the value of the unpresent pair is zero. But it does not assume anything while subtracting.

          irb> h1.merge(h2) {|key,old,new| old-new }
          
          => {'$'=>0,'other'=>200}
          
          irb> h1.merge(h2) {|key,old,new| new-old }
          
          => {'$'=>0,'other'=>200}
          

So we could expect the value of h1['other'] after the merge to be -200 in the second example. But it's not.

So in the project merging the hashes with a subtraction gave a wrong value for the first loop. (in the view)

A way around this, a quick one was to use every currency that was available in the system and instantiate the symbol with a zero value.

          def service_cost  
            cost, currency, s_cost = {}, '', 0
          
            Currency.all.collect do |c|
              cost[c.symbol] = 0
            end
            # rest of the function goes here
          
          end
          
Comments

No comments yet, you can be the first one to comment one the subject, to do that use the form below.

Comment on the subject

The email address and name is required but the email will not be published. You can use textile markup. Also gravatars are used if they exist.

In the secret type in the month and year without any separator. If January 2008 then 12008. It's a simple antispam protection.