Repository: josdejong/mathjs
Pull request #699 (2016-08-06 19:09:44)

Split unit


Patch:
diff --git a/docs/datatypes/units.md b/docs/datatypes/units.md
index 1e020efc1..20e2b4024 100644
--- a/docs/datatypes/units.md
+++ b/docs/datatypes/units.md
@@ -198,6 +198,14 @@ An object `{mathjs: 'Unit', value: number, unit: string, fixPrefix: boolean}`,
 where the property `mathjs` and `fixPrefix` are optional.
 Used when deserializing a unit, see [Serialization](../core/serialization.md).
 
+### unit.splitUnit(parts)
+Split a unit into the specified parts. For example:
+
+```js
+var u = math.Unit(1, 'm');
+u.splitUnit(['ft', 'in']);    // 3 feet,3.3700787401574765 inch
+```
+
 ### unit.to(unitName)
 Convert the unit to a specific unit name. Returns a clone of
 the unit with a fixed prefix and unit.
diff --git a/docs/reference/functions.md b/docs/reference/functions.md
index 79b3d645f..d10724e08 100644
--- a/docs/reference/functions.md
+++ b/docs/reference/functions.md
@@ -236,6 +236,7 @@ Function | Description
 Function | Description
 ---- | -----------
 [math.to(x,&nbsp;unit)](functions/to.md) | Change the unit of a value.
+[math.splitUnit(array)](functions/splitUnit.md) | Split a unit into parts.
 
 ## Utils functions
 
diff --git a/lib/type/unit/Unit.js b/lib/type/unit/Unit.js
index 981e5441d..9518a5769 100644
--- a/lib/type/unit/Unit.js
+++ b/lib/type/unit/Unit.js
@@ -11,6 +11,7 @@ function factory (type, config, load, typed, math) {
   var divide    = load(require('../../function/arithmetic/divideScalar'));
   var pow       = load(require('../../function/arithmetic/pow'));
   var abs       = load(require('../../function/arithmetic/abs'));
+  var fix       = load(require('../../function/arithmetic/fix'));
   var equal     = load(require('../../function/relational/equal'));
   var isNumeric = load(require('../../function/utils/isNumeric'));
   var format    = load(require('../../function/string/format'));
@@ -856,7 +857,12 @@ function factory (type, config, load, typed, math) {
    * @return {number | BigNumber | Fraction} Returns the unit value
    */
   Unit.prototype.toNumeric = function (valuelessUnit) {
-    var other = this.to(valuelessUnit);
+    var other = this;
+    if(valuelessUnit) {
+      // Allow getting the numeric value without converting to a different unit
+      other = this.to(valuelessUnit);
+    }
+
     if(other._isDerived()) {
       return other._denormalize(other.value);
     }
@@ -1165,6 +1171,37 @@ function factory (type, config, load, typed, math) {
     return bestPrefix;
   };
 
+  /**
+   * Returns an array of units whose sum is equal to this unit
+   * @memberof Unit
+   * @param {Array} [parts] An array of strings or valueless units. 
+   *
+   *   Example:
+   *
+   *   var u = new Unit(1, 'm');
+   *   u.splitUnit(['feet', 'inch']);
+   *     [ 3 feet, 3.3700787401575 inch ]
+   *
+   * @return {Array} An array of units.
+   */
+  Unit.prototype.splitUnit = function(parts) {
+
+    var x = this.clone();
+    var ret = [];
+    for(var i=0; i<parts.length; i++) {
+      x = x.to(parts[i]);
+      if(i==parts.length-1) break;
+      // fix rounds a number towards 0
+      var fixedVal = fix(x.toNumeric());
+      var y = new Unit(fixedVal, parts[i].toString());
+      ret.push(y);
+      x = subtract(x, y);
+    }
+    ret.push(x);
+
+    return ret;
+  };
+
   var PREFIXES = {
     NONE: {
       '': {name: '', value: 1, scientific: true}
diff --git a/lib/type/unit/function/splitUnit.js b/lib/type/unit/function/splitUnit.js
new file mode 100644
index 000000000..13c7443df
--- /dev/null
+++ b/lib/type/unit/function/splitUnit.js
@@ -0,0 +1,31 @@
+'use strict';
+
+var deepMap = require('../../../utils/collection/deepMap');
+
+function factory (type, config, load, typed) {
+
+  /**
+  * Returns an array of units whose sum is equal to this unit
+  * @memberof Unit
+  * @param {Array} [parts] An array of strings or valueless units.
+  *
+  *   Example:
+  *
+  *   splitUnit(new Unit(1, 'm'), ['feet', 'inch']);
+  *     [ 3 feet, 3.3700787401575 inch ]
+  *
+  * @return {Array} An array of units.
+  */
+
+  var splitUnit = typed('splitUnit', {
+    'Unit, Array': function(unit, parts) {
+      return unit.splitUnit(parts);
+    }
+  });
+
+  return splitUnit;
+
+}
+
+exports.name = 'splitUnit';
+exports.factory = factory;
diff --git a/lib/type/unit/index.js b/lib/type/unit/index.js
index 320847e61..23a1273d0 100644
--- a/lib/type/unit/index.js
+++ b/lib/type/unit/index.js
@@ -8,6 +8,9 @@ module.exports = [
   // create new units
   require('./function/createUnit'),
 
+  // split units
+  require('./function/splitUnit'),
+
   // physical constants
   require('./physicalConstants')
 ];


Test patch:
diff --git a/test/type/unit/Unit.test.js b/test/type/unit/Unit.test.js
index 9df28a957..cc9613cda 100644
--- a/test/type/unit/Unit.test.js
+++ b/test/type/unit/Unit.test.js
@@ -1146,4 +1146,16 @@ describe('Unit', function() {
     });
   });
 
+  describe('splitUnit', function() {
+    it('should split a unit into parts', function() {
+      assert.equal((new Unit(1, 'm')).splitUnit(['ft', 'in']).toString(), "3 ft,3.3700787401574765 in");
+      assert.equal((new Unit(-1, 'm')).splitUnit(['ft', 'in']).toString(), "-3 ft,-3.3700787401574765 in");
+      assert.equal((new Unit(1, 'm/s')).splitUnit(['m/s']).toString(), "1 m / s");
+      assert.equal((new Unit(1, 'm')).splitUnit(['ft', 'ft']).toString(), "3 ft,0.280839895013123 ft");
+      assert.equal((new Unit(1.23, 'm/s')).splitUnit([]).toString(), "1.23 m / s");
+      assert.equal((new Unit(1, 'm')).splitUnit(['in', 'ft']).toString(), "39 in,0.030839895013123605 ft");
+      assert.equal((new Unit(1, 'm')).splitUnit([ new Unit(null, 'ft'), new Unit(null, 'in') ]).toString(), "3 ft,3.3700787401574765 in");
+    });
+  });
+
 });
diff --git a/test/type/unit/function/splitUnit.test.js b/test/type/unit/function/splitUnit.test.js
new file mode 100644
index 000000000..c1d424648
--- /dev/null
+++ b/test/type/unit/function/splitUnit.test.js
@@ -0,0 +1,14 @@
+var assert = require('assert');
+var math = require('../../../../index');
+var splitUnit = math.splitUnit;
+var Unit = math.type.Unit;
+
+describe('splitUnit', function() {
+    it('should split a unit into parts', function() {
+      assert.equal(splitUnit(new Unit(1, 'm'), ['ft', 'in']).toString(), "3 ft,3.3700787401574765 in");
+      assert.equal(splitUnit(new Unit(-1, 'm'), ['ft', 'in']).toString(), "-3 ft,-3.3700787401574765 in");
+      assert.equal(splitUnit(new Unit(1, 'm/s'), ['m/s']).toString(), "1 m / s");
+
+      assert.equal(math.eval('splitUnit(1 m, [ft, in])').toString(), "3 ft,3.3700787401574765 in");
+    });
+});