Подтвердить что ты не робот

Единица измерения конверсионной библиотеки

Каков наилучший/самый элегантный способ абстрагироваться от преобразования единиц измерения в клиенте на основе предпочтительной для пользователя единицы измерения?

Например, допустим, что пользователь. Предпочтительная единица измерения "метрическая", а предпочтение пользователя B - "имперское".

Теперь скажем, что я вычислил область чего-то в квадрате метров. Когда я перехожу к отображению значения, мне нужно использовать разные коэффициенты пересчета для каждого пользователя (например, "1 метр = 1.09361 ярдов" ). Или сказать, что я рассчитал объем жидкости в mL. Взгляд пользователя B будет рассчитываться с использованием преобразования "236.588237 mL = 1 US cup".

Есть ли существующая библиотека javascript, которую кто-нибудь знает об этом, обрабатывает эти тривиальные преобразования UOM?

4b9b3361

Ответ 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

Ответ 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