gentooboontoo / js-quantities

JavaScript library for quantity calculation and unit conversion
http://gentooboontoo.github.io/js-quantities/
MIT License
396 stars 102 forks source link

numerator and denominator getters to return a Qty object #46

Open maackle opened 9 years ago

maackle commented 9 years ago

Currently it seems .numerator and .denominator are used internally for consistency checking. However it would be useful to easily have access to the units of the numerator or denominator.

My particular use case is dealing with rates (unit/time). Ideally this is what I'd like to do:

var dt = Qty('1 hour');
var rate = Qty('1 kg/day');
var amount = rate.mul(dt).to(q.numeratorUnit);

I want to see how many kg I get in one hour. In general, if I define a rate of any kind, and I want to integrate it over some time interval by multiplying a time unit, I want to get back the units of the numerator. Note that the .to(...) is necessary because otherwise the units in this case will be unreduced: kg * hour / day

The best solution I've come up with came from poking around in the internals:

var dt = Qty('1 hour');
var rate = Qty('1 kg/day');
var numeratorUnit = Qty({scalar: 1, numerator: a.numerator});
var amount = rate.mul(dt).to();

This gets me the units of kg that I want.

Even if there were a getter for numerator that just returned the stringified unit name instead of the array [ '<kg>' ], that would make it much easier to work with. I know it's a nitpicky request but I wonder if there's any way to do this, or any other plans that would make this possible.

BTW, is there a simplify() or reduceUnits() function that would realize that day and hour are of the same dimension and scale the value accordingly?

gentooboontoo commented 9 years ago

No, there's not (publicly at least). It really seems to be a bug. Units are meant to be automatically simplified.

Qty('3 lbs').div(Qty('2 kg')).toString(); // -> "0.68..." as intended
Qty('3 lbs*m').div(Qty('2 kg')).toString(); // -> "1.5 lbs*m/kg", wrong scalar and units

I'll try to track down that bug. Thank you for reporting.

Juansasa commented 8 years ago

Any progress on this ?

luzlab commented 7 years ago

BTW - If you want to force the conversion manually, both of the following work (they generate a new Qty object with the units set to 'kg'.

var amount = rate.mul(dt).to('kg')
var amount = rate.mul(dt).to(rate.numerator[0].slice(1,-1))

That said, I dont think units should be automatically simplified in all cases. Ideally, we want to do all the math one shot. There's nothing inherently wrong with storing long arrays of numerators and denominators as long as it's easy to simplify the Qty when needed.

xixixao commented 4 years ago

Division isn't simplifying at all, which is what I thought would be a huge selling point of this library.

> Qty('6.67259e-11 N*m2/kg2').toBase().toString()
'6.67259e-11 kg*m3/s2*kg2'

It should be '6.67259e-11 m3/s2*kg' instead.

xixixao commented 4 years ago

You can use something like this, if you want full normalization:

function toBase(x) {
  const y = x.toBase();
  const ORDER = [
    "<kilogram>",
    "<meter>",
    "<second>",
    "<ampere>",
    "<kelvin>",
    "<mole>",
    "<candela>",
  ];
  const num = new Map();
  const den = new Map();
  y.numerator.forEach((unit) => {
    num.set(unit, 1 + (num.get(unit) || 0));
  });
  y.denominator.forEach((unit) => {
    den.set(unit, 1 + (den.get(unit) || 0));
  });
  const numSimpl = [];
  const denSimpl = [];
  ORDER.forEach((unit) => {
    const n = (num.get(unit) || 0) - (den.get(unit) || 0);
    if (n > 0) {
      for (let i = 0; i < n; i++) {
        numSimpl.push(unit);
      }
    } else if (n < 0) {
      for (let i = 0; i > n; i--) {
        denSimpl.push(unit);
      }
    }
  });
  return Qty({ scalar: y.scalar, numerator: numSimpl, denominator: denSimpl });
}

which gives

> toBase(Qty('6.67259e-11 N*m2/kg2')).toString()
'6.67259e-11 m3/kg*s2'