From 97ff95b32c8d49a8ea40c92a5e0d4135e8da3bd1 Mon Sep 17 00:00:00 2001 From: Eric Mansfield Date: Wed, 8 Jan 2020 02:23:23 -0700 Subject: [PATCH] Added baseName option to createUnit (#1708) * Fixed unit base recognition and formatting for user-defined units * Added baseName option to createUnit * Trailing spaces * Fixed line in docs --- docs/datatypes/units.md | 6 ++++-- src/type/unit/Unit.js | 29 +++++++++++++++++++------- test/unit-tests/type/unit/Unit.test.js | 10 +++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/docs/datatypes/units.md b/docs/datatypes/units.md index 0931f40ca..cabc78b31 100644 --- a/docs/datatypes/units.md +++ b/docs/datatypes/units.md @@ -115,7 +115,7 @@ math.createUnit('furlong', '220 yards') math.evaluate('1 mile to furlong') // 8 furlong ``` -If you cannot express the new unit in terms of any existing unit, then the second argument can be omitted. In this case, a new base unit is created: +If you cannot express the new unit in terms of any existing unit, then the second argument can be omitted. In this case, a new *base unit* is created: ```js // A 'foo' cannot be expressed in terms of any other unit. @@ -129,6 +129,7 @@ The second argument to `createUnit` can also be a configuration object consistin * **prefixes** A `string` indicating which prefixes math.js should use with the new unit. Possible values are `'none'`, `'short'`, `'long'`, `'binary_short'`, or `'binary_long'`. Default is `'none'`. * **offset** A value applied when converting to the unit. This is very helpful for temperature scales that do not share a zero with the absolute temperature scale. For example, if we were defining fahrenheit for the first time, we would use: `math.createUnit('fahrenheit', {definition: '0.555556 kelvin', offset: 459.67})` * **aliases** An array of strings to alias the new unit. Example: `math.createUnit('knot', {definition: '0.514444 m/s', aliases: ['knots', 'kt', 'kts']})` +* **baseName** A `string` that specifies the name of the new dimension in case one needs to be created. Every unit in math.js has a dimension: length, time, velocity, etc. If the unit's `definition` doesn't match any existing dimension, or it is a new base unit, then `createUnit` will create a new dimension with the name `baseName` and assign it to the new unit. The default is to append `'_STUFF'` to the unit's name. If the unit already matches an existing dimension, this option has no effect. An optional `options` object can also be supplied as the last argument to `createUnits`. Currently only the `override` option is supported: @@ -145,7 +146,8 @@ For example: ```js math.createUnit( { foo: { - prefixes: 'long' + prefixes: 'long', + baseName: 'essence-of-foo' }, bar: '40 foo', baz: { diff --git a/src/type/unit/Unit.js b/src/type/unit/Unit.js index 6de54bf73..76f9116e0 100644 --- a/src/type/unit/Unit.js +++ b/src/type/unit/Unit.js @@ -3095,11 +3095,24 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ * createUnitSingle('acre', new Unit(43560, 'ft^2')) * * @param {string} name The name of the new unit. Must be unique. Example: 'knot' - * @param {string, Unit} definition Definition of the unit in terms of existing units. For example, '0.514444444 m / s'. - * @param {Object} options (optional) An object containing any of the following properties: - * prefixes {string} "none", "short", "long", "binary_short", or "binary_long". The default is "none". - * aliases {Array} Array of strings. Example: ['knots', 'kt', 'kts'] - * offset {Numeric} An offset to apply when converting from the unit. For example, the offset for celsius is 273.15 and the offset for farhenheit is 459.67. Default is 0. + * @param {string, Unit, Object} definition Definition of the unit in terms + * of existing units. For example, '0.514444444 m / s'. Can be a Unit, a string, + * or an Object. If an Object, may have the following properties: + * - definition {string|Unit} The definition of this unit. + * - prefixes {string} "none", "short", "long", "binary_short", or "binary_long". + * The default is "none". + * - aliases {Array} Array of strings. Example: ['knots', 'kt', 'kts'] + * - offset {Numeric} An offset to apply when converting from the unit. For + * example, the offset for celsius is 273.15 and the offset for farhenheit + * is 459.67. Default is 0. + * - baseName {string} If the unit's dimension does not match that of any other + * base unit, the name of the newly create base unit. Otherwise, this property + * has no effect. + * + * @param {Object} options (optional) An object containing any of the following + * properties: + * - override {boolean} Whether this unit should be allowed to override existing + * units. * * @return {Unit} */ @@ -3126,6 +3139,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ let offset = 0 let definition let prefixes + let baseName if (obj && obj.type === 'Unit') { defUnit = obj.clone() } else if (typeof (obj) === 'string') { @@ -3136,6 +3150,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ definition = obj.definition prefixes = obj.prefixes offset = obj.offset + baseName = obj.baseName if (obj.aliases) { aliases = obj.aliases.valueOf() // aliases could be a Matrix, so convert to Array } @@ -3171,7 +3186,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ let newUnit = {} if (!defUnit) { // Add a new base dimension - const baseName = name + '_STUFF' // foo --> foo_STUFF, or the essence of foo + baseName = baseName || name + '_STUFF' // foo --> foo_STUFF, or the essence of foo if (BASE_DIMENSIONS.indexOf(baseName) >= 0) { throw new Error('Cannot create new base unit "' + name + '": a base unit with that name already exists (and cannot be overridden)') } @@ -3234,7 +3249,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({ } } if (!anyMatch) { - const baseName = name + '_STUFF' // foo --> foo_STUFF, or the essence of foo + baseName = baseName || name + '_STUFF' // foo --> foo_STUFF, or the essence of foo // Add the new base unit const newBaseUnit = { dimensions: defUnit.dimensions.slice(0) } newBaseUnit.key = baseName diff --git a/test/unit-tests/type/unit/Unit.test.js b/test/unit-tests/type/unit/Unit.test.js index 768800ab4..037b7ad77 100644 --- a/test/unit-tests/type/unit/Unit.test.js +++ b/test/unit-tests/type/unit/Unit.test.js @@ -1164,6 +1164,16 @@ describe('Unit', function () { assert.strictEqual('jabberwocky_STUFF' in Unit.BASE_UNITS, true) assert.strictEqual(math.evaluate('4 mile^5/minute').format(4), '240 jabberwocky') }) + + it('should use baseName', function () { + Unit.createUnitSingle('truck', { baseName: 'VEHICLE' }) + Unit.createUnitSingle('speedy', { definition: '1 truck/day', baseName: 'VEHICLE_PRODUCTION_RATE' }) + assert('VEHICLE' in Unit.BASE_UNITS) + assert('VEHICLE_PRODUCTION_RATE' in Unit.BASE_UNITS) + assert(new Unit(1, 'truck').hasBase('VEHICLE')) + assert(new Unit(1, 'truck/day').hasBase('VEHICLE_PRODUCTION_RATE')) + assert.strictEqual(math.evaluate('10 truck/hr').format(4), '240 speedy') + }) }) describe('createUnit', function () {