Repository: josdejong/mathjs
Pull request #817 (2017-03-29 14:48:46)

Added Unit.prototype.toSI
Adds the Unit method `toSI`, which returns a clone of the unit converted to base SI units. Also adds docs and unit tests. 


Patch:
diff --git a/docs/datatypes/units.md b/docs/datatypes/units.md
index 563aa8ba6..b63470ece 100644
--- a/docs/datatypes/units.md
+++ b/docs/datatypes/units.md
@@ -226,6 +226,9 @@ specified unit (a unit with optional prefix but without value).
 The type of the returned value depends on how the unit was created and 
 can be `number`, `Fraction`, or `BigNumber`.
 
+### unit.toSI()
+Returns a clone of a unit represented in SI units. Works with units with or without a value.
+
 ### unit.toString()
 Get a string representation of the unit. The function will
 determine the best fitting prefix for the unit.
diff --git a/lib/type/unit/Unit.js b/lib/type/unit/Unit.js
index 97313fea2..3f096dc83 100644
--- a/lib/type/unit/Unit.js
+++ b/lib/type/unit/Unit.js
@@ -989,6 +989,40 @@ function factory (type, config, load, typed, math) {
     this.isUnitListSimplified = true;
   };
 
+  Unit.prototype.toSI = function() {
+
+    var ret = this.clone();
+
+    var proposedUnitList = [];
+
+    // Multiple units or units with powers are formatted like this:
+    // 5 (kg m^2) / (s^3 mol)
+    // Build an representation from the base units of the SI unit system
+    var missingBaseDim = false;
+    for(var i=0; i<BASE_DIMENSIONS.length; i++) {
+      var baseDim = BASE_DIMENSIONS[i];
+      if(Math.abs(ret.dimensions[i] || 0) > 1e-12) {
+        if(UNIT_SYSTEMS["si"].hasOwnProperty(baseDim)) {
+          proposedUnitList.push({
+            unit: UNIT_SYSTEMS["si"][baseDim].unit,
+            prefix: UNIT_SYSTEMS["si"][baseDim].prefix,
+            power: ret.dimensions[i] || 0
+          });
+        }
+        else {
+          throw new Error("Cannot express custom unit " + baseDim + " in SI units");
+        }
+      }
+    }
+
+    // Replace this unit list with the proposed list
+    ret.units = proposedUnitList;
+
+    ret.isUnitListSimplified = true;
+
+    return ret;
+  }
+
   /**
    * Get a string representation of the units of this Unit, without the value.
    * @memberof Unit


Test patch:
diff --git a/test/type/unit/Unit.test.js b/test/type/unit/Unit.test.js
index 192282c8a..78bf71823 100644
--- a/test/type/unit/Unit.test.js
+++ b/test/type/unit/Unit.test.js
@@ -1173,4 +1173,31 @@ describe('Unit', function() {
     });
   });
 
+  describe('toSI', function() {
+    it('should return a clone of the unit', function() {
+      var u1 = Unit.parse('3 ft');
+      var u2 = u1.toSI();
+      assert.equal(u1 === u2, false);
+    });
+
+    it('should return the unit in SI units', function() {
+      assert.equal(Unit.parse('3 ft').toSI().format(10), "0.9144 m");
+    });
+
+    it('should return SI units for valueless units', function() {
+      assert.equal(Unit.parse('ft/minute').toSI().toString(), "m / s");
+    });
+
+    it('should return SI units for custom units defined from other units', function() {
+      Unit.createUnit({foo:'3 kW'}, {override: true});
+      assert.equal(Unit.parse('42 foo').toSI().toString(), "1.26e+5 (kg m^2) / s^3");
+    });
+
+    it('should throw if custom unit not defined from existing units', function() {
+      Unit.createUnit({baz:''}, {override:true});
+      assert.throws(function() { Unit.parse('10 baz').toSI(); }, /Cannot express custom unit/);
+    });
+
+  });
+
 });