UNPKG

measure-convert

Version:

JS/TS package for managing units of measurement. Convert, add, subtract, multiply, divide, and compare units of measurement.

577 lines (417 loc) 17.9 kB
# Unit Conversion Library A robust and comprehensive TypeScript library for handling a wide array of measurement units and performing precise unit conversions. Whether you're working on scientific computations, engineering applications, or everyday unit conversions, this library provides the tools you need with accuracy and ease. ## Table of Contents - [Features](#features) - [Supported Measurement Types](#supported-measurement-types) - [Installation](#installation) - [Usage](#usage) - [Basic Conversion](#basic-conversion) - [Formatting with Decimal Places](#formatting-with-decimal-places) - [Adding and Subtracting Measurements](#adding-and-subtracting-measurements) - [Checking Proximity with `closeTo`](#checking-proximity-with-closeto) - [Database Serialization and Hydration](#database-serialization-and-hydration) - [Quick Reference: Unit Types and Common Units](#quick-reference-unit-types-and-common-units) - [API Reference](#api-reference) - [Measurement Class](#measurement-class) - [Unit Classes](#unit-classes) - [Examples](#examples) - [Testing](#testing) - [Contributing](#contributing) - [License](#license) ## Features - **Wide Range of Units:** Supports over 20 different measurement types, including Temperature, Speed, Length, Mass, Volume, and more. - **Accurate Conversions:** Utilizes precise conversion factors to ensure minimal rounding errors. - **Intelligent Formatting:** Smart decimal place handling with customizable precision control. - **Scaling & Ratios:** Calculate ratios between measurements and scale values for nutrition calculations and recipe scaling. - **Database Integration:** Seamless serialization and hydration for database storage (MongoDB, Parse Server, etc.). - **Extensible Design:** Easily add new units and measurement types as needed. - **Comprehensive Testing:** Ensures reliability with thorough unit tests covering all functionalities. - **TypeScript Support:** Leverages TypeScript for type safety and enhanced developer experience. ## Supported Measurement Types Your library currently supports the following measurement types: - **Acceleration** (`UnitAcceleration`) - **Angle** (`UnitAngle`) - **Area** (`UnitArea`) - **Concentration (Mass)** (`UnitConcentrationMass`) - **Dispersion** (`UnitDispersion`) - **Duration** (`UnitDuration`) - **Electric Charge** (`UnitElectricCharge`) - **Electric Current** (`UnitElectricCurrent`) - **Electric Potential Difference** (`UnitElectricPotentialDifference`) - **Electric Resistance** (`UnitElectricResistance`) - **Energy** (`UnitEnergy`) - **Frequency** (`UnitFrequency`) - **Fuel Efficiency** (`UnitFuelEfficiency`) - **Illuminance** (`UnitIlluminance`) - **Information Storage** (`UnitInformationStorage`) - **Length** (`UnitLength`) - **Mass** (`UnitMass`) - **Power** (`UnitPower`) - **Pressure** (`UnitPressure`) - **Speed** (`UnitSpeed`) - **Temperature** (`UnitTemperature`) - **Volume** (`UnitVolume`) Each measurement type comes with its own set of units, enabling precise and context-specific conversions. ## Installation Install the library via npm: ```bash npm install measure-convert ``` Or using Yarn: ```bash yarn add measure-convert ``` ## Usage ### Basic Conversion To perform a basic unit conversion, create a `Measurement` instance with a value and its corresponding unit, then convert it to the desired unit. ```typescript import { Measurement, UnitTemperature } from 'measure-convert'; // Convert 100°C to Fahrenheit const tempCelsius = new Measurement(100, UnitTemperature.celsius); const tempFahrenheit = tempCelsius.converted(UnitTemperature.fahrenheit); console.log(`${tempCelsius.value}°C is equal to ${tempFahrenheit.value}°F`); ``` **Output:** ```100°C is equal to 212°F``` ### Formatting with Decimal Places Control how values are displayed with intelligent decimal formatting: ```typescript import { Measurement, UnitMass } from 'measure-convert'; const weight = new Measurement(4.0000000000123, UnitMass.grams); // Intelligent defaults (0 decimals for values ≥100, 2 for 1-10, etc.) console.log(weight.shortLabel); // "4 g" // Force specific decimal places console.log(weight.getShortLabel(2)); // "4.00 g" // Smart rounding avoids showing 0 for significant values const smallWeight = new Measurement(0.5, UnitMass.grams); console.log(smallWeight.getShortLabel(0)); // "0.5 g" (not "0 g") // Show approximation for very small values const tinyWeight = new Measurement(0.09, UnitMass.grams); console.log(tinyWeight.getShortLabel(0, true)); // "~0 g" ``` ### Adding and Subtracting Measurements You can add or subtract two measurements, provided they are of the same measurement type. ```typescript import { Measurement, UnitMass, UnitVolume } from 'measure-convert'; // Adding masses (cooking example) const flour = new Measurement(120, UnitMass.grams); const sugar = new Measurement(0.25, UnitMass.pounds); const totalMass = flour.add(sugar.converted(UnitMass.grams)); console.log(totalMass.shortLabel); // "233.4 g" // Adding volumes const water = new Measurement(1, UnitVolume.cups); const milk = new Measurement(250, UnitVolume.milliliters); const totalVolume = water.add(milk.converted(UnitVolume.cups)); console.log(totalVolume.getShortLabel(2)); // "2.04 cup" ``` ### Checking Proximity with `closeTo` Determine if two measurements are within a specified tolerance of each other. ```typescript import { Measurement, UnitLength } from 'measure-convert'; // Check if 100 meters is close to 328.084 feet within 0.1 meters tolerance const length1 = new Measurement(100, UnitLength.meters); const length2 = new Measurement(328.084, UnitLength.feet); const isClose = length1.closeTo(length2, 0.1); console.log(`Are the lengths close? ${isClose}`); ``` **Output:** ```Are the lengths close? true``` ## Quick Reference: Unit Types and Common Units ### Mass (UnitMass) ```typescript import { UnitMass } from 'measure-convert'; UnitMass.grams // g UnitMass.kilograms // kg UnitMass.milligrams // mg UnitMass.ounces // oz UnitMass.pounds // lb ``` ### Volume (UnitVolume) ```typescript import { UnitVolume } from 'measure-convert'; UnitVolume.milliliters // mL UnitVolume.liters // L UnitVolume.teaspoons // tsp UnitVolume.tablespoons // tbsp UnitVolume.cups // cup UnitVolume.fluidOunces // fl oz UnitVolume.pints // pt UnitVolume.quarts // qt UnitVolume.gallons // gal ``` ### Temperature (UnitTemperature) ```typescript import { UnitTemperature } from 'measure-convert'; UnitTemperature.celsius // °C UnitTemperature.fahrenheit // °F UnitTemperature.kelvin // K ``` ### Length (UnitLength) ```typescript import { UnitLength } from 'measure-convert'; UnitLength.millimeters // mm UnitLength.centimeters // cm UnitLength.meters // m UnitLength.kilometers // km UnitLength.inches // in UnitLength.feet // ft UnitLength.yards // yd UnitLength.miles // mi ``` ## API Reference ### Measurement Class The `Measurement` class represents a value with an associated unit and provides methods for conversion and comparison. #### Constructor ```typescript constructor(value: number, unit: Unit) ``` - `value`: The numerical value of the measurement. - `unit`: The unit of the measurement. #### Methods - **`converted(targetUnit: V): Measurement<V>`** Converts the current measurement to the `targetUnit`. ```typescript converted<V extends Unit>(targetUnit: V): Measurement<V> ``` - **`getShortLabel(decimalPlaces?: number, showApprox?: boolean): string`** Returns a formatted string with the unit symbol, with optional decimal place control. ```typescript getShortLabel(decimalPlaces?: number, showApprox?: boolean): string ``` - **`getLongLabel(decimalPlaces?: number, showApprox?: boolean): string`** Returns a formatted string with the full unit name, with optional decimal place control. ```typescript getLongLabel(decimalPlaces?: number, showApprox?: boolean): string ``` - **`add(other: Measurement<U>): Measurement<U>`** Adds another measurement of the same type. ```typescript add(other: Measurement<U>): Measurement<U> ``` - **`subtract(other: Measurement<U>): Measurement<U>`** Subtracts another measurement of the same type. ```typescript subtract(other: Measurement<U>): Measurement<U> ``` - **`ratioTo(other: Measurement<U>): number`** Calculates the ratio between two measurements of the same type. ```typescript ratioTo(other: Measurement<U>): number ``` - **`scaledBy(factor: number): Measurement<U>`** Scales a measurement by a factor while preserving the unit. ```typescript scaledBy(factor: number): Measurement<U> ``` - **`closeTo(other: Measurement<U>, tolerance: number): boolean`** Checks if another measurement is within a specified `tolerance`. ```typescript closeTo(other: Measurement<U>, tolerance: number): boolean ``` - **`Static closeTo(measurement1: Measurement<U>, measurement2: Measurement<U>, tolerance: number): boolean`** Static method to check if two measurements are within a specified `tolerance`. ```typescript static closeTo<U extends Unit>(measurement1: Measurement<U>, measurement2: Measurement<U>, tolerance: number): boolean ``` - **`Static hydrateAuto(data): Measurement<any>`** Auto-detects unit type and restores a `Measurement` object from serialized data (recommended). ```typescript static hydrateAuto(data: SerializedMeasurement): Measurement<any> ``` - **`Static hydrate(UnitClass, data): Measurement<U>`** Restores a `Measurement` object from serialized data when you know the unit type (for type safety). ```typescript static hydrate<T extends typeof Unit, U extends InstanceType<T>>(UnitClass: T, data: SerializedMeasurement): Measurement<U> ``` - **`Static detectUnitType(data): string | null`** Detects what unit type a serialized measurement belongs to (for advanced use cases). ```typescript static detectUnitType(data: SerializedMeasurement): string | null ``` ### Unit Classes Each measurement type has its own `Unit` class, encapsulating the specific units and their conversion factors. Below are brief descriptions of each supported `Unit` class. - **UnitAcceleration** - **UnitAngle** - **UnitArea** - **UnitConcentrationMass** - **UnitDispersion** - **UnitDuration** - **UnitElectricCharge** - **UnitElectricCurrent** - **UnitElectricPotentialDifference** - **UnitElectricResistance** - **UnitEnergy** - **UnitFrequency** - **UnitFuelEfficiency** - **UnitIlluminance** - **UnitInformationStorage** - **UnitLength** - **UnitMass** - **UnitPower** - **UnitPressure** - **UnitSpeed** - **UnitTemperature** - **UnitVolume** Each `Unit` class provides predefined units relevant to its measurement type. For example, `UnitTemperature` includes Celsius, Fahrenheit, and Kelvin. #### Example: UnitTemperature ```typescript import { Unit } from 'measure-convert'; // Access predefined temperature units const celsius = UnitTemperature.celsius; const fahrenheit = UnitTemperature.fahrenheit; const kelvin = UnitTemperature.kelvin; ``` ## Examples ### Cooking/Nutrition Examples ```typescript import { Measurement, UnitMass, UnitVolume, UnitTemperature } from 'measure-convert'; // Recipe scaling const flourPerServing = new Measurement(120, UnitMass.grams); const additionalFlour = new Measurement(360, UnitMass.grams); // 3 more servings const flourFor4Servings = flourPerServing.add(additionalFlour); console.log(`Flour needed: ${flourFor4Servings.getShortLabel(0)}`); // Output: Flour needed: 480 g // Unit conversion for international recipes const butter = new Measurement(0.5, UnitMass.pounds); const butterInGrams = butter.converted(UnitMass.grams); console.log(`${butter.shortLabel} = ${butterInGrams.shortLabel}`); // Output: 0.5 lb = 227 g // Volume conversions const milk = new Measurement(1, UnitVolume.cups); const milkInMl = milk.converted(UnitVolume.milliliters); console.log(`${milk.shortLabel} = ${milkInMl.getShortLabel(0)}`); // Output: 1 cup = 240 mL // Temperature for cooking const ovenTemp = new Measurement(350, UnitTemperature.fahrenheit); const ovenTempC = ovenTemp.converted(UnitTemperature.celsius); console.log(`Oven: ${ovenTemp.shortLabel} = ${ovenTempC.getShortLabel(0)}`); // Output: Oven: 350°F = 177°C ``` ### Precision Control for Nutritional Data ```typescript import { Measurement, UnitMass } from 'measure-convert'; // Nutritional values often need precise formatting const protein = new Measurement(23.456789, UnitMass.grams); const fat = new Measurement(0.123456, UnitMass.grams); // Standard nutrition label formatting (1 decimal place) console.log(`Protein: ${protein.getShortLabel(1)}`); // "23.5 g" console.log(`Fat: ${fat.getShortLabel(1)}`); // "0.1 g" // Scientific precision (3 decimal places) console.log(`Precise protein: ${protein.getShortLabel(3)}`); // "23.457 g" // Handle trace amounts const trace = new Measurement(0.001, UnitMass.grams); console.log(`Trace amount: ${trace.getShortLabel(0, true)}`); // "~0 g" ``` ### Scaling for Nutrition Calculations Perfect for nutrition apps, recipe scaling, and food portion calculations: ```typescript import { Measurement, UnitMass, UnitEnergy, UnitVolume } from 'measure-convert'; // Calculate nutrition per serving from per-100g values const energyPer100g = new Measurement(400, UnitEnergy.calories); const foodPortion = new Measurement(20, UnitMass.grams); const baseReference = new Measurement(100, UnitMass.grams); // Calculate the ratio and scale the energy const ratio = foodPortion.ratioTo(baseReference); // 0.2 const portionEnergy = energyPer100g.scaledBy(ratio); // 80 calories console.log(`Energy per portion: ${portionEnergy.shortLabel}`); // Output: Energy per portion: 80 Cal // Works with different units - volume-based nutrition const energyPer100ml = new Measurement(250, UnitEnergy.kilocalories); const servingSize = new Measurement(0.25, UnitVolume.liters); const mlReference = new Measurement(100, UnitVolume.milliliters); const volumeRatio = servingSize.ratioTo(mlReference); // 2.5 const servingEnergy = energyPer100ml.scaledBy(volumeRatio); // 625 kcal console.log(`Energy per serving: ${servingEnergy.shortLabel}`); // Output: Energy per serving: 625 kCal // Recipe scaling example const flourPerServing = new Measurement(120, UnitMass.grams); const scaledFlour = flourPerServing.scaledBy(4); // Scale for 4 servings console.log(`Flour needed: ${scaledFlour.shortLabel}`); // Output: Flour needed: 480 g ``` ### Database Serialization and Hydration When storing `Measurement` objects in databases (MongoDB, Parse Server, etc.), they get serialized as plain objects and lose their methods. Use the hydration system to restore full functionality: ```typescript import { Measurement, UnitEnergy } from 'measure-convert'; // Original measurement const energy = new Measurement(100, UnitEnergy.kilocalories); // When saved to database, it becomes a plain object like: const dbData = { "value": 100, "unit": { "name": "Kilocalories", "symbol": "kCal", "description": "Unit of measure for energy", "baseUnitConversionFactor": 4184 } }; // SIMPLEST: Auto-detect and hydrate in one step const measurement = Measurement.hydrateAuto(dbData); console.log(measurement.shortLabel); // "100 kCal" console.log(measurement.converted(UnitEnergy.joules).shortLabel); // "418400 J" // ALTERNATIVE: When you need type safety, specify the unit class const typedMeasurement = Measurement.hydrate(UnitEnergy, dbData); // Returns Measurement<UnitEnergy> with full TypeScript typing // ADVANCED: Just detect the type (for complex logic) const unitType = Measurement.detectUnitType(dbData); console.log(unitType); // "UnitEnergy" ``` **When to use hydration:** - Loading measurements from databases (MongoDB, Parse Server, Firebase, etc.) - Receiving measurement data from APIs as JSON - Restoring measurements from localStorage or sessionStorage - Any time you have a plain object that was originally a `Measurement` **The hydrated measurement has all the original functionality:** - `.converted()` - Unit conversion - `.add()`, `.subtract()` - Arithmetic operations - `.shortLabel`, `.longLabel` - Formatted display - `.closeTo()`, `.equals()` - Comparisons ## Testing The library includes a comprehensive test suite to ensure all functionalities work as expected. Tests cover unit conversions, arithmetic operations, comparison methods, and error handling. ### Running Tests Ensure you have all dependencies installed: ```bash npm install ``` Run the test suite using Jest: ```bash npm test ``` ### Test Coverage To generate a test coverage report, run: ```bash npm test -- --coverage ``` This command will display coverage statistics and generate a detailed report, ensuring that all parts of your codebase are adequately tested. ## Contributing Contributions are welcome! Whether it's fixing bugs, improving documentation, or adding new features, your help is appreciated. ### How to Contribute 1. **Fork the Repository** 2. **Create a New Branch** ```bash git checkout -b feature/YourFeatureName ``` 3. **Make Your Changes** 4. **Run Tests** Ensure all tests pass: ```bash npm test ``` 5. **Commit Your Changes** ```bash git commit -m "Add feature: YourFeatureName" ``` 6. **Push to Your Fork** ```bash git push origin feature/YourFeatureName ``` 7. **Create a Pull Request** Submit your changes for review. ### Guidelines - Follow the existing coding style and conventions. - Ensure all tests pass before submitting. - Provide clear and concise commit messages. - Update documentation as necessary. ## License This project is licensed under the [Apache License 2.0](LICENSE). ---