SitePoint Sponsor

User Tag List

Results 1 to 6 of 6

Hybrid View

  1. #1
    SitePoint Member
    Join Date
    Apr 2009
    Location
    Marin CA
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Displaying fractions in Ruby

    I'd like to be able to store fractions in the database, so that people can type things such as 1/2 or 1 1/3. Given the fractions I am interested in (half, quarter, third) I will just store the numbers as integer twelvths.

    Example: if I wanted 1 1/2 store 18, if I wanted 3 2/3 store as 44 etc.

    So then how would I format to display 1 1/2 and 3 2/3 etc. in views?

  2. #2
    SitePoint Member
    Join Date
    Apr 2009
    Location
    Australia
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here's the first solution using a whopping case statement for the fractional part. This is not ideal, but it gives an idea of what's going on in order to understand the second better solution.

    Code:
    def frac(num)
      str = (num / 12).to_s
      frac = num % 12
      frac_str = case frac
        when 0 then ''
        when 1 then '1/12'
        when 2 then '1/6'
        when 3 then '1/4'
        when 4 then '1/3'
        when 5 then '5/12'
        when 6 then '1/2'
        when 7 then '7/12'
        when 8 then '2/3'
        when 9 then '3/4'
        when 10 then '5/6'
        when 11 then '11/12'
      end
      return [str, frac_str].join(' ')
    end
    I use division and modulus in the first 2 lines to find the whole number and fractional bits. Then I use a case statement on the fractional part to map to a nice string representation. The return statement uses a join to avoid having messy if/then/else statements in the event the number is divisible by 12.

    Here's the second solution that gets rid of the massive case statement by utilising the mathematical concept known as the greatest common divisor.

    Code:
    def frac(num)
      str = (num / 12).to_s
      common_divisor = 12.gcd(num % 12)
      frac_str = if common_divisor == 12
        nil
      else
        [num % 12 / common_divisor, 12 / common_divisor].join('/')
      end
      return [str, frac_str].compact.join(' ')
    end
    The first line is the same as the first code sample. The second line tells me the greatest common divisor of the modulus and 12. Then I use the GCD to normalise the numerator and denominator provided the number doesn't doesn't divide by 12 entirely. There's a little bit of trickery/ugliness in the code to make sure the string formatting looks appropriate on the way out.

    I hope this helps!

  3. #3
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If all you want to do is allow users multiple ways of inputting information, I'd use regular expressions to determine the user input format and deal with it. You should then be able to store the number in a standard format thereby removing the complication of storing twelfths and allowing the user to enter any fraction.

    Here a sample script that uses a class to shows how you can do that:
    Code:
    class FractionTest
      NUMBER_AND_FRACTION = /(\d+)\s+(\d+\/\d+)/
      FRACTION_ONLY = /(\d+)\/(\d+)/
      NUMBER_ONLY = /(\d+\.*\d*)/
    
      def test(number)
        case number
        when NUMBER_AND_FRACTION
          number_and_fraction(number)
        when FRACTION_ONLY
          fraction_only(number)
        when NUMBER_ONLY
          number_only(number)
        else
          puts "no match"
        end
        puts "--------------"
      end
    
      private
    
      def number_and_fraction(number)
        parts = number.match(NUMBER_AND_FRACTION)
        result = parts[1].to_f + process_fraction(parts[2])
        puts "#{number} contains the number #{parts[1]} "
        puts "and the fraction #{parts[2]} "
        puts "and is equal to #{result}."
      end
    
      def fraction_only(number)
        result = process_fraction(number)
        puts "#{number} contains the fraction #{number} "
        puts "and is equal to #{result}."
      end
    
      def number_only(number)
        result = number.match(NUMBER_ONLY)[0].to_f
        puts "#{number} contains the number #{number}"
        puts "and is equal to #{result}."
      end
    
      def process_fraction(fraction)
        parts = fraction.match(FRACTION_ONLY)
        numerator = parts[1].to_f
        denominator = parts[2].to_f
        return numerator / denominator
      end
    
    end
    
    ft = FractionTest.new
    
    ft.test("1 1/2")
    ft.test("1/2")
    ft.test("1")
    ft.test("4.34")

  4. #4
    SitePoint Member
    Join Date
    Apr 2009
    Location
    Marin CA
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Reggie, that was really helpful on the fractions thanks. I am a relatively new coder so I will go thru it with a fine tooth comb. Quick follow up question:

    Should I store the inputs in my table (Quantity Column) as a integer or a float?

    Do I take the input from users in the view as a text as:

    <p>
    Measurement Quantitybr />
    <&#37;= f.text_field :quantity %>
    </p>

    Many thanks again.

  5. #5
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cberry1971 View Post
    Should I store the inputs in my table (Quantity Column) as a integer or a float?
    It is always best to store the data in the format that best matches the data itself. As the numbers you are storing contain a decimal component, integer would not be appropriate.

    Float is the obvious solution. However, you should be aware that Float stores numbers as bits rather than as base 10 decimals, and you can get small quirks transferring back and forth between the float and the decimal system your human users will use. For many situations (for me the vast majority) float is perfectly satisfactory. However, if precise accuracy is required, you should consider using a decimal field.

    Quote Originally Posted by cberry1971 View Post
    Do I take the input from users in the view as a text
    Yes. The nature of web forms is that data passed from the client browser to the server, is passed as text. However, Rails (and in particular ActiveRecord) is clever enough to automatically convert the text into the correct format for the field when data is inserted into the database, if you use the built-in functions to do that. So you shouldn't have to worry about it.

  6. #6
    SitePoint Member
    Join Date
    Apr 2009
    Location
    Australia
    Posts
    8
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cberry1971 View Post
    Should I store the inputs in my table (Quantity Column) as a integer or a float?
    Store the number as an integer multiple of 1/12. So 1/6 would be stored as 2 I.e. 2/12.

    Float will produce rounding errors in special cases.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •