# Return input as a partial fraction

I need to write a function that, when given an input (string, float, or int), returns that input as a reduced partial fraction, and it needs to accept a wide array of inputs:

1.5 =>  "1 1/2"
5/2 => "2 1/2"
"1/3" => "1/3"
5 => "5"
"6 1/3" => "6 1/3"
0 => "0"


I've written this function using the Rational class, but was wondering if there were a better, more elegant solution:

def format_partial_fraction(fraction)
if fraction.include? "/" # to handle "6 1/3" => "6 1/3" (or maybe I'll just trust user input in this case)
fraction = fraction.split(" ").inject{|sum,x| sum.to_r + x.to_r }
end

rational = fraction.to_r
if rational == 0
return "0"
elsif rational < 1 # e.g. "1/3"
rational.to_s
else # e.g. "3 1/2" or just "3"
rational.to_i.to_s + ( rational%1 == 0 ? "" : " " + (rational%1).to_s)
end
end


Ruby 2.0.0, Rails 3.2.13

• I misread the title at first. I was disappointed when I found that no partial function is involved :) Still, good question. Jan 20, 2014 at 22:02
• f you are satisfied with any of the answers, you should select the one that was most helpful to you. Feb 27, 2014 at 20:38

Non-string input can lead to surprising results:

p (5/2).to_r #=> (2/1), integer division performed first
p (1.1).to_r #=> (2476979795053773/2251799813685248),  float can not be represented in binary


The last case is prevented by:

p 1.1.to_s.to_r #=> (11/10)


So I'd change rational = fraction.to_r to rational = fraction.to_s.to_r. But the first case is only handled correctly if it is a string to begin with:

p "5/2".to_r #=> (5/2)


I don't think there is a remedy for that, except accepting string or float input input only.

• Right. If the method-caller passes in 5/2 as an integer, there's nothing that can be done. Jan 21, 2014 at 1:19

this

fraction = fraction.split(" ").inject{|sum,x| sum.to_r + x.to_r }


can also be expressed like this:

fraction = fraction.split(" ").map(&:to_r).inject(:+)


this:

rational.to_i.to_s + ( rational%1 == 0 ? "" : " " + (rational%1).to_s)


is a bit long, and repeats the expression rational%1. I'd use a small private method to clean it up a bit:

rational.to_i.to_s + fractional_part_to_s(rational % 1)

...

def fraction_part_to_s(f)
if fractional_part == 0
""
else
fractional_part.to_s
end
end

• I like your first suggestion but I prefer the compactness of the original over your 2nd suggestion Jan 22, 2014 at 3:30
• @Jonah, Although I am not a fan of the trinary operator, it is a reasonable choice, and would serve just as well there. Jan 22, 2014 at 10:57
• on 2nd thought i do like giving that portion a name. i just really dislike full if else statements. you could do return f.to_s unless f == 0; "" Jan 22, 2014 at 11:10
• @Jonah, There are many ways to skin this cat Jan 22, 2014 at 11:13