开发者

Best way to write this calculation in Ruby?

What is the best way to write this calculation in Ruby?

amt = self.alt_inv - (self.alt_tax ? self.alt_tax : 0)
    - (self.alt_freight ? self.alt_freight : 0)
 开发者_如何学Go   - (self.misc1_amt ? self.misc1_amt : 0)
    - (self.misc2_amt ? self.misc2_amt : 0)


In Ruby, if something is not nil, it will return true in a boolean expression and its value in a calculation.

I'm not explaining it really well, but you can do something like this:

amt = alt_inv - 
  (alt_tax     || 0) -
  (alt_freight || 0) -
  (misc1_amt   || 0) -
  (misc2_amt   || 0)

This is a more concise way of doing the ternary you were originally using.


Edit:

I actually like Jed Schneider's answer better than my own. I'll not copy it here, since his answer deserves the upvote for its elegance.


Since amt is the difference between the sum of all the attributes and the alt_inv, you can sum the deductions and then subtract from alt_inv. reduce offers a clean way to do this. compact removes any nil values before performing the reduction.

Of course, attributes in this case can be expanded to whatever your needs are, even created dynamically from data, and in real life, I wouldn't use the variable attributes, obviously.

attributes = [alt_tax, alt_freight, misc1_amt, misc2amt]
amt = alt_inv - attributes.compact.reduce(:+)

reduce: http://apidock.com/ruby/Enumerable/reduce

compact: http://apidock.com/ruby/Array/compact


It might be cleaner if you could automatically initialize your object's values to 0 when the object was created, if those values were not supplied in the constructor. Otherwise, you need to do this sort of conditional logic everywhere in your objects these values are needed. Wouldn't you rather just do this?

amt = alt_inv - alt_tax - alt_freight - misc1_amt - misc2_amt 


A little variations from the solution of Jed, which is not needlessly complex, but more succinct IMHO.

Using compact and inject (the most common synonym of reduce) are not complex, and thoroughly used among Ruby programmers:

amt = [alt_tax, alt_freight, misc1_amt, misc2_amt].inject(alt_inv) do |result, attribute|
  result - (attribute || 0)
end

or:

amt = alt_inv - [alt_freight, misc1_amt, misc2_amt].compact.inject{|sum, n| sum + n }

which is an alternate way to write the inject(&:+) if you're uncomfortable with that syntax. If you're using Rails, you can substitute the inject with the sum method, or implement it yourself.


If you don't have local variable with same the name, instead of self.alt_tax you can write simply alt_tax without self.

Also the || operator returns the first operand when it's not nil otherwise it returns the second, so instead of:

alt_tax ? alt_tax : 0

you can write:

alt_tax || 0


Looks ok to me with two exceptions:

1) Ruby makes assumptions about statement endings.

Since

amt = self.alt_inv - (self.alt_tax ? self.alt_tax : 0)

is itself a valid statement, you need to either keep the minus on the prior line or escape the newline.

2) Ruby assumes "self" as the owner except for left side assignments. I suggest:

amt = alt_inv - 
  (alt_tax     ? alt_tax     : 0) -
  (alt_freight ? alt_freight : 0) -
  (misc1_amt   ? misc1_amt   : 0) -
  (misc2_amt   ? misc2_amt   : 0)


amt = alt_inv - alt_tax.to_f - alt_freight.to_f - misc1_amt.to_f - misc2_amt.to_f
nil.to_f = 0.0
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜