Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby way: catch division by zero

I have the following method to compute an average:

def compute_average(a,b,c,d,e)
  total = [a,b,c,d,e].sum.to_f
  average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
  average.round(2)
end

It's nothing special, but it has a problem that I expect all average equations have: it might divide by zero if inputs are all zero.

So, I thought of doing this:

def compute_average(a,b,c,d,e)
  total = [a,b,c,d,e].sum.to_f
  if total==0
    average = 0.00
  else
    average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
    average.round(2)
  end
end

... and that works, but it feels kludgy to me. Is there a more elegant, "Ruby Way" to avoid this division by zero problem?

What I'm wishing I had was an "unless then" operator, like...

average = numerator / denominator unless denominator == 0 then 0

Any suggestions?

like image 341
Andrew Avatar asked Apr 03 '11 16:04

Andrew


5 Answers

You can use nonzero?, as in:

def compute_average(a,b,c,d,e)
  total = [a,b,c,d,e].sum.to_f
  average = [a, 2*b, 3*c, 4*d, 5*e].sum / (total.nonzero? || 1)
end

More people would be more familiar with using the ternary operator (total == 0 ? 1 : total), so that's another possibility.

like image 75
Marc-André Lafortune Avatar answered Oct 31 '22 12:10

Marc-André Lafortune


It's common to rely on rescue to capture the exception, then to return the default value:

def compute_average(a, b, c, d, e)
  total = [a, b, c, d, e].sum.to_f
  average = [ a, 2*b, 3*c, 4*d, 5*e ].sum / total
  average.round(2)
  rescue ZeroDivisionError
    0.0
end

Also I'd write:

average = numerator / denominator unless denominator == 0 then 0

as

average = (denominator == 0) ? 0 : numerator / denominator
like image 22
the Tin Man Avatar answered Oct 31 '22 10:10

the Tin Man


While this is an outdated thread I thought I would chime in with a simple one liner you can use...

@average = variable1 / variable2 rescue 0
like image 41
Jonathan Reyes Avatar answered Oct 31 '22 11:10

Jonathan Reyes


def compute_average(a,b,c,d,e)
  total = (a+b+c+d+e).to_f
  total.zero? ? 0 : ((a + 2*b + 3*c + 4*d + 5*e) / total).round(2)
end
like image 3
Jakub Hampl Avatar answered Oct 31 '22 10:10

Jakub Hampl


To me, the cleanest way is:

numerator / denominator rescue 0

It also saves you from handling 0 / 0.

As @Andrew points out, this is only valid for integers. See the comments to this answer for more info.

like image 3
espinchi Avatar answered Oct 31 '22 12:10

espinchi