Inspired from a stackoverflow.com question.
Convert decimal amount to text string fraction in Javascript?
Code
/** * @author Larry Battle <http://bateru.com/news/contact-me> * @license MIT * @version 1.0 * @date May 08, 2012 * @purpose To provide a function that converts a decimal to a simplified fraction. * @info <http://bateru.com/news/2012/05/code-of-the-day-javascript-convert-decimal-to-a-simplified-fraction/> */ var mixin = {}; mixin.getKeys = function (obj) { var props = []; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { props.push(prop); } } return props; }; mixin.getFractionObject = (function () { var obj = { 0 : '0', 1 : '1' }, num = 10, den = 11, value; while (--num) { while (--den > 1) { value = (num / den).toFixed(3); if (value < 1) { obj[value] = num + "/" + den; } } den = 11; } obj.keys = mixin.getKeys(obj); return function () { return obj; }; } ()); mixin.getClosestNum = function (arr, val) { if (typeof arr !== "object" || !(arr.hasOwnProperty('length')) || isNaN(val)) { return false; } var i = arr.length, j = i - 1, minDiff = Math.abs(+val - arr[j]), diff; while (i--) { diff = Math.abs(+val - arr[i]); if (diff < minDiff) { minDiff = diff; j = i; } } return arr[j]; }; mixin.getFractionFromDecimal = function (dec) { if (isNaN(dec) || !isFinite(dec)) { return false; } if (!/\./.test(dec)) { return dec; } var fracs = mixin.getFractionObject(), matches = dec.toString().match(/(\d+)(\.\d+)/), fraction = fracs[mixin.getClosestNum(fracs.keys, Math.abs(+matches[2]))], sign = ( 0 < dec || (fraction == "0" && Math.abs(dec) < 1) ) ? '' : '-'; if (1 < Math.abs(dec)) { if (isNaN(fraction)) { fraction = +matches[1] + " " + fraction; } else { fraction = +matches[1] + (+fraction); } } return sign + fraction; }; |
Testcases and Demo
// Qunit Testcases $(document).ready(function(){ var runAllTests = function () { test("Test getKeys()", function () { var obj = {}; deepEqual([], getKeys(obj), "Empty objects should have no props."); obj.a = 1; deepEqual(['a'], getKeys(obj), "a should be returned as a property."); obj = { 1 : 1, 2 : 1, 3 : 1 } deepEqual(['1', '2', '3'], getKeys(obj), "1,2,3 should be properties."); }); test("Test getClosestNum()", function () { var arr = [0, 1, 2, 3, 10, 99, 1000]; equal(false, getClosestNum({}, 1), "First argument must be an array."); equal(false, getClosestNum(arr), "Second argument must be an defined."); equal(false, getClosestNum(arr, {}), "Second argument must be an number."); var runThisTest = function (nums, expect) { var i = expect.length; while (i--) { equal(getClosestNum(nums, expect[i][0]), expect[i][1], expect[i][0] + " is the closest to " + expect[i][1]); } }; runThisTest(arr, [ [-10, 0], [0.3, 0], [0.5, 1], [2.1, 2], [10.1, 10], [100, 99], [600, 1000] ]); }); test("Test getFractionObject()", function () { var fracs = getFractionObject(); var props = getKeys( fracs ); areAllNumbers = true; var i = props.length; while( i-- ){ if( isNaN( props[ i ] ) ){ areAllNumbers = false; } } ok( areAllNumbers, "getFractionObject() returns an object with all properties as a number." ); equal( fracs[ 0 ], "0", "0.500 should be 0" ); equal( fracs[ '0.333' ], "1/3", "0.333 should be 1/3" ); equal( fracs[ '0.500' ], "1/2", "0.500 should be 1/2" ); equal( fracs[ '0.900' ], "9/10", "0.900 should be 9/10" ); equal( fracs[ 1 ], "1", "0.500 should be 1" ); }); test("Test getFractionFromDecimal() with invalid inputs", function(){ equal(getFractionFromDecimal( "ten" ), false ); equal(getFractionFromDecimal( NaN ), false ); equal(getFractionFromDecimal( undefined ), false ); equal(getFractionFromDecimal( Infinity ), false ); }); test("Test getFractionFromDecimal() with only decimal values <= 1", function(){ equal(getFractionFromDecimal(0.001), "0" ); equal(getFractionFromDecimal(0.23), "2/9" ); equal(getFractionFromDecimal(0.335), "1/3" ); equal(getFractionFromDecimal(0.756), "3/4" ); equal(getFractionFromDecimal(0.995), "1" ); }); test("Test getFractionFromDecimal() with integers", function(){ equal(getFractionFromDecimal(12), "12" ); equal(getFractionFromDecimal(3), "3" ); }); test("Test getFractionFromDecimal() with negative values", function(){ equal(getFractionFromDecimal(-0.001), "0" ); equal(getFractionFromDecimal(-0.23), "-2/9" ); equal(getFractionFromDecimal(-0.335), "-1/3" ); equal(getFractionFromDecimal(-0.756), "-3/4" ); equal(getFractionFromDecimal(-0.995), "-1" ); }); test("Test getFractionFromDecimal() with numbers bigger than 1.", function(){ equal(getFractionFromDecimal(10.001), "10" ); equal(getFractionFromDecimal(-10.001), "-10" ); equal(getFractionFromDecimal(12.23), "12 2/9" ); equal(getFractionFromDecimal(3.335), "3 1/3" ); equal(getFractionFromDecimal(99.756), "99 3/4" ); equal(getFractionFromDecimal(34.995), "35" ); }); }; runAllTests(); }); |
Example
mixin.getFractionFromDecimal(-99.756); // returns “-99 3/4”
(Page view Count: 211)