JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 114
  • Score
    100M100P100Q85976F
  • License MIT

Simple data converter

Package Exports

  • parakeet-mapper

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (parakeet-mapper) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Parakeet Mapper

npm npm bundle size (minified) dependencies (minified)

Simple data conversion library


npm install --save parakeet-mapper

For more options see installation


What is this?

It's a small collection of utility functions and types that help with mapping (transforming) data.

You can find it useful if:

  • You work with complex data objects and tired of it
  • You need to quickly and efficiently transform objects or tuples regularly
  • Your data models need additional functionality
  • You need a type-safe way of initializing a class from an object
  • You need to convert data types back and forth multiple times
  • You don't like your backend's API and GraphQL is not an option 😁

Installation

Install as dependency

npm install --save parakeet-mapper
# or
yarn add parakeet-mapper

Import and use

ES

import { mapTypes, mapFactory } from 'parakeet-mapper'

CommonJS

const { mapTypes, mapFactory } = require('parakeet-mapper');

Script tag

<script src="https://unpkg.com/parakeet-mapper"></script>

...

<script>
  // global variable parakeetMapper

  const { mapTypes, mapFactory } = parakeetMapper;
</script>

API

parakeet-mapper exposes several helpers to deal with type conversions.
All of them use the TypeMap interface to communicate.

name overloads description
mapFactory 3 Accepts a TypeMap. Returns a function that accepts one input and returns an output converted using rules defined in the TypeMap.
mapTypes 3 Same as mapFactory, but instead of returning a function, accepts input as its first argument and returns an output right away.
Convertable 2 Class mixin. Allows creation of convertable classes.

TypeMap

object

It is a set of rules that define how the output type is made from the input type.\

The rules are simple:\

  • Each key corresponds to a key in the output.
  • Each value tells what to assign to that key from the input.
    • true = the value is assigned from the same key in the input.
    • A string = the value is assigned from this string key in the input.
    • An object = the value is assigned from this object's first key in the input and is processed using the value as converter.
    • An array of a single element = the first element in the array is used as converter for the input value by the output key.
    • A function = the value is mapped using this function from the input.

Examples

const input = {
  transferred: 'foo',
  renamed: 'bar',
  converted: '42',
  mapped: [1, 1, 2],
  omitted: 'won\'t transfer to output'
};

const TypeMap = {
  // Transferred straight to the output
  transferred: true,

  // Renamed from `renamed` into `outputRenamed`
  outputRenamed: 'renamed',

  // Renamed from `converted` into `convertedNumber`
  // and converted from string to number
  convertedNumber: { converted: Number },

  // Mapped using a function and also renamed.
  mappedSum: input => input.mapped.reduce((a, b) => a + b),

  mappedPlusConverted: input => input.mapped.reduce((a, b) => a + b) + Number(input.converted)
};

/* output */ {
  transferred: 'foo',
  outputRenamed: 'bar',
  convertedNumber: 42, // Number('42')
  mappedSum: 4,
  mappedPlusConverted: 46 // 4 + 42
  // notice the absence of `ommited` property
}

"The same key" object/array shorthand

new in v2.1.2

It's not necessary to specify the correct key in the conversion object:

const input = {
  number: '42',
};

const TypeMap = {
  // Simply converts from string to number using the `Number` function
  number: { Number },
};

/* output */ {
  number: 42, // Number('42')
  // Notice that the property name stayed the same,
  // even though we used a `{ Number: Number }` shorthand.
}

It only works if the input has the same property key as the output.

Can also be written using the array (tuple) syntax:

const input = {
  number: '42',
};

const TypeMap = {
  // Simply converts from string to number using the `Number` function
  number: [Number],
};

/* output */ {
  number: 42, // Number('42')
  // Notice that the property name stayed the same,
  // even though we used a `{ Number: Number }` shorthand.
}

mapFactory

function

A factory function that produces a converter from a TypeMap:

import { mapFactory } from 'parakeet-mapper';

const inputToOutput = mapFactory(TypeMap);

const output = inputToOutput(input); /* {
  transferred: 'foo',
  outputRenamed: 'bar',
  convertedNumber: 42, // Number('42')
  mappedSum: 4,
  mappedPlusConverted: 46 // 4 + 42
  // notice the absence of `ommited` property
} */

Overloads

This function has 3 overloads, all of which are needed for type safety and type inference (TypeScript).

There are basically 3 typed use-cases of using this function (hence 3 overloads):

  1. Input and output types are known, and TypeMap needs to convert them precisely.
    declare const input: InputType;
    const inputConverter = mapFactory<InputType, OutputType>(TypeMap);
    // output is OutputType now
  2. Input type is known, output needs to be inferred from the TypeMap.
    The first call without arguments is a noop. Reasons: first, second.
    declare const input: InputType; // The empty braces are here due to TS issues.
    const inputConverter = mapFactory<InputType>()(TypeMap);
  3. Input and output types are known, but the output type needs to be modified slightly.
    The first call without arguments is a noop. Reasons: first, second.
    declare const input: InputType; // The empty braces are here due to TS issues.
    const inputConverter = mapFactory<InputType, OutputType>()(TypeMap);

mapTypes

function

Basically, a mapFactory, called in-place.

First Argument Second Argument
The input object Corresponding TypeMap
import { mapTypes } from 'parakeet-mapper';

const output = mapTypes(input, TypeMap); /* {
  transferred: 'foo',
  outputRenamed: 'bar',
  convertedNumber: 42, // Number('42')
  mappedSum: 4,
  mappedPlusConverted: 46 // 4 + 42
  // notice the absence of `ommited` property
} */

// Same as
// const output = mapFactory(TypeMap)(input);

Overloads

There are 3. All are semantically the same as the overloads of mapFactory, including the noop call:

/* 1 */ mapTypes<InputType, OutputType>(input, TypeMap);
/* 2 */ mapTypes<InputType>()(input, TypeMap);
/* 3 */ mapTypes<InputType, OutputType>()(input, TypeMap);

Wait

function
new in v2.1

A complementary function to mapFactory that helps to flatten out promises

It produces a converter that returns a flat promise from a converter that returns an object with promises.

In a typical situation, when some convertations are asyncronous, you'd end up with this:

import { mapTypes } from 'parakeet-mapper';

// Imagine that this is requesting something from an API and returns a promise
const requestFromApi = (value) => Promise.resolve(value);

const input = {
  a: ['a'],
  b: 42,
  c: 'c'
};

const getAandBfromAPI = mapFactory({
  b: true,
  a: { a: requestFromApi },
  c: { c: requestFromApi }
});

const output = getAandBfromAPI(input);
// Result:
/* {
  a: Promise<['a']>,
  b: 42,
  c: Promise<'b'>
} */
// Not very comfortable to await every single value after this

Now with wait:

import { wait } from 'parakeet-mapper';

const waitForAandB = wait(getAandBfromAPI);


const output = getAandBfromAPI(input);
// Result:
/* Promise<{
  a: ['a'],
  b: 42,
  c: 'b'
}> */
// Much more useful now

flattenPromises

function
new in v2.1

Internally used in wait, flattens top-level promises in an object:

import { flattenPromises } from 'parakeet-mapper';

const objWithPromises = {
  a: [Promise.resolve('a')],
  b: 42,
  c: Promise.resolve('b')
};

const flat = flattenPromises(objWithPromises);
// Result
/* Promise<{
  a: ['a'],
  b: 42,
  c: 'b'
}> */

Convertable

function
new in v2.0

Allows to create classes from converters.
This makes possible adding extra functionality, including reverse convertations.

import { Convertable, mapFactory } from 'parakeet-mapper';

const inputConverter = () => mapFactory(mapTypes);

class Output extends Convertable(inputConverter) {}

const output = new Output(input);

console.log(output); /*
> Output {
    transferred: 'foo',
    outputRenamed: 'bar',
    convertedNumber: 42,
    mappedSum: 4,
    mappedPlusConverted: 46
  }
*/

Accepts a function that returns a converter as its only argument.
Returns a Convertable class with all the required functionality.

Can also infer arguments from its converter factory:

import { Convertable, mapFactory } from 'parakeet-mapper';

const input: InputType = {
  foo: 'foo',
  bar: 'bar'
}

const inputConverter = (convertFoo?: boolean) => mapFactory<InputType>()({
  zoo: convertFoo,
  zar: convertFoo ? 'bar' : 'foo'
});

class Output extends Convertable(inputConverter) {}

const outputWithFoo = new Output(input, /* convertFoo? */ true);
console.log(outputWithFoo); /*
> Output {
    zoo: 'foo',
    zar: 'bar'
  }
*/

const outputWithoutFoo = new Output(input, /* convertFoo? */ false);
console.log(outputWithoutFoo); /*
> Output {
    zar: 'foo'
  }
*/

And accept a reverse converter:

import { Convertable, mapFactory } from 'parakeet-mapper';

const input: InputType = {
  foo: 'foo',
  bar: 'bar'
}

const inputConverter = (convertFoo?: boolean) => mapFactory<InputType>()({
  zoo: convertFoo,
  zar: convertFoo ? 'bar' : 'foo'
});

const outputConverter = (convertZoo?: boolean) => mapFactory<OutputType>()({
  foo: convertZoo,
  bar: convertZoo ? 'zar' : 'zoo'
});

class Output extends Convertable(inputConverter, outputConverter) {}

const output = new Output(input, /* convertFoo? */ true);
console.log(output); /*
> Output {
    zoo: 'foo',
    zar: 'bar'
  }
*/

// Convert back to input type
const newInput = output.toInput(/* convertZoo? */ true);
console.log(newInput); /*
> {
    foo: 'foo',
    bar: 'bar'
  }
*/

Convertable class

constructor

Accepts an input as its first argument and converter factory parameters as other spread arguments.

toInput

Available only if reverse converter was passed into the Convertable.

Accepts a spread of reverse converter arguments

Convertable.createConverter

static

A converter factory, passed to the Convertable.

Convertable.reverseConverter

static

A reverse converter factory, passed to the Convertable.