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
Simple data conversion library
npm install --save parakeet-mapperFor 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
GraphQLis not an option 😁
Installation
Install as dependency
npm install --save parakeet-mapper
# or
yarn add parakeet-mapperImport 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):
- 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
- 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);
- 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 inv2.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 thisNow 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 nowflattenPromises
function
new inv2.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 inv2.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.