Единица измерения конверсионной библиотеки
Каков наилучший/самый элегантный способ абстрагироваться от преобразования единиц измерения в клиенте на основе предпочтительной для пользователя единицы измерения?
Например, допустим, что пользователь. Предпочтительная единица измерения "метрическая", а предпочтение пользователя B - "имперское".
Теперь скажем, что я вычислил область чего-то в квадрате метров. Когда я перехожу к отображению значения, мне нужно использовать разные коэффициенты пересчета для каждого пользователя (например, "1 метр = 1.09361 ярдов" ). Или сказать, что я рассчитал объем жидкости в mL
. Взгляд пользователя B будет рассчитываться с использованием преобразования "236.588237 mL = 1 US cup".
Есть ли существующая библиотека javascript, которую кто-нибудь знает об этом, обрабатывает эти тривиальные преобразования UOM?
Ответы
Ответ 1
Вот немного script Я бросил вместе только для этого. Он обрабатывает все преобразования SI для граммов, байтов, метров и литров, а также я добавил унции и фунты в качестве примера единиц, не относящихся к SI. Чтобы добавить больше, вам необходимо:
- Добавить базовый тип в список "units" для элементов, следующих за SI или
- Добавьте коэффициенты преобразования для элементов, которые не соответствуют SI
Использование:
$u(1, 'g').as('kg').val(); // converts one gram to kg
Вы можете получить значение с помощью .val(), строкового представления с использованием .toString() или полной информации через .debug()
(function () {
var table = {};
window.unitConverter = function (value, unit) {
this.value = value;
if (unit) {
this.currentUnit = unit;
}
};
unitConverter.prototype.as = function (targetUnit) {
this.targetUnit = targetUnit;
return this;
};
unitConverter.prototype.is = function (currentUnit) {
this.currentUnit = currentUnit;
return this;
};
unitConverter.prototype.val = function () {
// first, convert from the current value to the base unit
var target = table[this.targetUnit];
var current = table[this.currentUnit];
if (target.base != current.base) {
throw new Error('Incompatible units; cannot convert from "' + this.currentUnit + '" to "' + this.targetUnit + '"');
}
return this.value * (current.multiplier / target.multiplier);
};
unitConverter.prototype.toString = function () {
return this.val() + ' ' + this.targetUnit;
};
unitConverter.prototype.debug = function () {
return this.value + ' ' + this.currentUnit + ' is ' + this.val() + ' ' + this.targetUnit;
};
unitConverter.addUnit = function (baseUnit, actualUnit, multiplier) {
table[actualUnit] = { base: baseUnit, actual: actualUnit, multiplier: multiplier };
};
var prefixes = ['Y', 'Z', 'E', 'P', 'T', 'G', 'M', 'k', 'h', 'da', '', 'd', 'c', 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y'];
var factors = [24, 21, 18, 15, 12, 9, 6, 3, 2, 1, 0, -1, -2, -3, -6, -9, -12, -15, -18, -21, -24];
// SI units only, that follow the mg/kg/dg/cg type of format
var units = ['g', 'b', 'l', 'm'];
for (var j = 0; j < units.length; j++) {
var base = units[j];
for (var i = 0; i < prefixes.length; i++) {
unitConverter.addUnit(base, prefixes[i] + base, Math.pow(10, factors[i]));
}
}
// we use the SI gram unit as the base; this allows
// us to convert between SI and English units
unitConverter.addUnit('g', 'ounce', 28.3495231);
unitConverter.addUnit('g', 'oz', 28.3495231);
unitConverter.addUnit('g', 'pound', 453.59237);
unitConverter.addUnit('g', 'lb', 453.59237);
window.$u = function (value, unit) {
var u = new window.unitConverter(value, unit);
return u;
};
})();
console.log($u(1, 'g').as('kg').debug());
console.log($u(1, 'kg').as('g').debug());
console.log($u(1, 'g').as('mg').debug());
console.log($u(1, 'mg').as('g').debug());
console.log($u(1, 'mg').as('kg').debug());
console.log($u(1, 'g').as('oz').debug());
console.log($u(1, 'g').as('lb').debug());
console.log($u(1, 'oz').as('lb').debug());
console.log($u(1, 'lb').as('g').debug());
// this last one throws an exception since you can't convert liters to mg
console.log($u(1, 'l').as('mg').debug());
Я перевел это на небольшое репо на Github, поэтому, если кто-то хочет улучшить/улучшить, он может это сделать:
https://github.com/jerodvenemafm/jsunitconverter
Ответ 2
Вы можете проверить этот порт блоков Ruby на Javascript:
https://github.com/gentooboontoo/js-quantities
Ответ 3
Вдохновленный многими сообщениями по этому вопросу и простотой и малости MomentJs, я начал работать над этим:
MeasurementJs
Он не включает все упомянутые преобразования, но вы можете легко расширить объекты DEFINITIONS
и MeasurementJs.Units.*
для любого возможного преобразования, которое вам нравится.
Ответ 4
Я просто оставлю это здесь...
Простой метод JS для преобразования почти всех имперских/стандартных измерений расстояния/длины
;;if (!window.hasOwnProperty('convertImperialMetric')) {
function convertImperialMetric () {
var metrics = convertImperialMetric.metrics,
imperials = convertImperialMetric.imperials,
args = arguments,
conversionTypes = { imperial: 'imperial', metric: 'metric' },
toFixed = false, toFixedX = 2,
intX, typImp, typMet, conType = 'metric',
$ret;
conversionTypes.i = conversionTypes.imp = conversionTypes.imperial;
conversionTypes.m = conversionTypes.met = conversionTypes.metric;
function setVarz(c) {
for (i in c) {
var a = c[i];
switch (typeof a) {
case "boolean":
toFixed = a;
break;
case "number":
void 0 == intX ? intX = a : toFixedX = a;
break;
case "string":
isNaN(parseFloat(a)) || void 0 != intX ? imperials.hasOwnProperty(a) ? typImp = a : metrics.hasOwnProperty(a) ? typMet = a : conversionTypes.hasOwnProperty(a) && (conType = conversionTypes[a]) : intX = parseFloat(a);
break;
case "object":
if (a instanceof Array) setVarz.apply(this, [a]);
else if (a instanceof Object)
for (h in a) {
var b = a[h];
conversionTypes.hasOwnProperty(h) ? conType = conversionTypes[h] : imperials.hasOwnProperty(h) ? (typImp =
h, void 0 != intX || isNaN(parseFloat(b)) || (intX = parseFloat(b))) : metrics.hasOwnProperty(h) ? (typMet = h, void 0 != intX || isNaN(parseFloat(b)) || (intX = parseFloat(b))) : setVarz.apply(this, [
[b]
])
}
}
}
};
setVarz(args);
if (!isNaN(parseFloat(intX)) && imperials.hasOwnProperty(typImp) && metrics.hasOwnProperty(typMet) && conversionTypes.hasOwnProperty(conType)) {
if (conType == 'metric') {
var inches = intX * imperials[typImp],
centimeters = inches * 2.54;
$ret = centimeters * metrics[typMet];
}
else if (conType == 'imperial') {
var centimeters = intX / metrics[typMet],
inches = centimeters / 2.54;
$ret = inches / imperials[typImp];
}
}
return toFixed ? parseFloat($ret.toFixed(toFixedX)) : $ret;
}
convertImperialMetric.imperials = {
inches: 1,
feet: 12,
yards: 36,
miles: 63360,
nautical: 72913.4
};
convertImperialMetric.metrics = {
yocto: 10000000000000000000000,
zepto: 10000000000000000000,
atto: 10000000000000000,
femto: 10000000000000,
pico: 10000000000,
nano: 10000000,
micro: 10000,
milli: 10,
centi: 1,
deci: .1,
meter: .01,
deka: .001,
hecto: .0001,
kilo: .00001,
mega: .00000001,
giga: .00000000001,
tera: .00000000000001,
peta: .00000000000000001,
exa: .00000000000000000001,
zetta: .00000000000000000000001,
yotta: .00000000000000000000000001
};
}
Примеры использования:
convertImperialMetric(12, 'inches', 'centi'); // Results in: 30.48
convertImperialMetric(30.48, 'centi', 'inches', 'i'); // Results in: 12
convertImperialMetric('1200000', 'inches', 'kilo'); // Results in: 30.480000000000004
convertImperialMetric('1200000', 'inches', 'kilo', true); // Results in: 30.48
convertImperialMetric('1200000', 'inches', 'kilo', true, 1); // Results in: 30.5
convertImperialMetric([ 12, 'inches', 'centi' ]); // Results in: 30.48
convertImperialMetric([ '12', 'inches', 'centi' ]); // Results in: 30.48
convertImperialMetric({ inches: 12, anyVariableName: 'centi' }); // Results in: 30.48
convertImperialMetric({ inches: '12', anyVariableName: 'centi' }); // Results in: 30.48
convertImperialMetric({ inches: 12, centi: 'anyValue' }); // Results in: 30.48
convertImperialMetric({ inches: '12', centi: 'anyValue' }); // Results in: 30.48
convertImperialMetric({ inches: '12', to: 'centi', type: 'metric', toFixed: true, toFX: 1 }); // Results in: 30.5
convertImperialMetric({ feet: 1 }, 'centi'); // Results in: 30.48
convertImperialMetric({ centi: '30.48' }, 'inches', 'imperial'); // Results in: 12
convertImperialMetric({ meter: '.3048' }, 'inches', 'imperial'); // Results in: 12
jsFiddle/jsFiddle/show
Ответ 5
Сколько разных единиц конвертируется? Звучит так, как будто вы пишете конкретное приложение, которое я принимаю, поскольку вам нужны только несколько разных типов преобразования (область, объем и т.д.).
Если это так, скорее всего, вы сможете просто найти конверсию для тех, которые вам нужны, и закодировать их в классе для себя. Загрузка полной библиотеки javascript только для выполнения нескольких конкретных умножений может быть чрезмерной.
Ответ 6
Здесь что-то расширяемое и краткое, которое создает таблицу поиска, чтобы производительность была хорошей.
core = {};
core.mixin = function (dst, src) { if (src) for (var i in src) dst[i] = src[i]; return dst; }
function UnitConverter(meter) {
var feetPerMeter = 3.2808399, feetPerMile = 5280;
this.meter = core.mixin({ km: 1000, mi: feetPerMile / feetPerMeter, ft: 1 / feetPerMeter, m: 1 }, meter);
var t = [], i = 0;
for (t[i++] in this.meter);
this.table = [];
for (i = 0; i < t.length; i++)
for (j = 0; j < t.length; j++) {
var key1 = t[i], key2 = t[(i + j + 1) % t.length];
this.table[key1 + "/" + key2] = this.meter[key2] / this.meter[key1];
}
};
with(UnitConverter) {
prototype = new Object();
prototype.convert = function () {
switch (arguments.length) {
case 1:
return this.table[arguments[0]];
case 2:
return arguments[0] * this.table[arguments[1]];
case 3:
return arguments[0] * this.meter[arguments[1]] / this.meter[arguments[2]];
}
return Number.NaN;
}
}
Использование
Number.converter = new UnitConverter({ nm: 1852 }); // extent to include nautical miles
Number.units = Number.converter.table;
var km = 1;
var mi = km * Number.units["mi/km"];
var m = Number.converter.convert(mi, "mi", "m");
var ft = Number.converter.convert(m, "ft/m");
m = Number.converter.convert(ft, "ft", "m");
ft = m / Number.converter.convert("m/ft");
km = ft * Number.units["km/ft"];
nm = Number.converter.convert(km, "km", "nm");
Ответ 7
Библиотека "measure" преобразует типы единиц (объем, массу и т.д.) и поддерживает несколько систем единиц (US, Imperial, Metric)
// volume
measure('1 teaspoon').teaspoons() // 1
measure('1 1/2 tsp.').teaspoons() // 1.5
measure('2 cups and 1 pint').quarts() // 1
// mass
measure('1 pound').ounces() // 16
measure('1 1/2 oz.').ounces() // 1.5
// operations
measure('1 teaspoon').add('1 teaspoon').teaspoons();
measure('6 1/2 teaspoon').divide(2).teaspoons(); // 3.25
https://github.com/dubbs/measure