6
\$\begingroup\$

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

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

2 Answers 2

3
\$\begingroup\$

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.

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

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
\$\endgroup\$
4
  • \$\begingroup\$ I like your first suggestion but I prefer the compactness of the original over your 2nd suggestion \$\endgroup\$
    – Jonah
    Jan 22, 2014 at 3:30
  • \$\begingroup\$ @Jonah, Although I am not a fan of the trinary operator, it is a reasonable choice, and would serve just as well there. \$\endgroup\$ Jan 22, 2014 at 10:57
  • \$\begingroup\$ 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; "" \$\endgroup\$
    – Jonah
    Jan 22, 2014 at 11:10
  • \$\begingroup\$ @Jonah, There are many ways to skin this cat \$\endgroup\$ Jan 22, 2014 at 11:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.