npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

dee-mapper

v1.1.1

Published

Object to object mapper

Downloads

10

Readme

Dee Mapper

npm npm Coverage Status dependency Status devDependency Status Build Status Known Vulnerabilities node

Object to Object mapper for NodeJS.

Table of contents

Installation

npm install dee-mapper --save

NodeJS >=6.0.0 is required.

Usage

To use the mapper you need to accomplish 3 simple steps:

  1. Create mapper instance.
  • Just import dee-mapper and call it with new keyword.
  1. Register new mapping.
  • Specify fields convention, mapping method name, source object type, destination object type and configuration callback (optionally). Configuration callback is used for mapping customizations.
  1. Call the method from step 2 on a mapper instance.

The example of code:

const Mapper = require('dee-mapper');
const mapper = new Mapper(); // step 1: create mapper instance

// step 2: register new map instance
mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', Object, Object, (map) => {
  // here 'map' is a mapping instance
  // you can configure the instance to customize mapping
   
  map.ignoreField('field_3')
    .mapField('field_2', obj => obj.field2.toUpperCase())
    .mapFieldByPath('field_1', 'field1');
});

// step 3: call the method from step 2 on a mapper instance
const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3'
});
console.log(result); // { field_1: 'test1', field_2: 'TEST2' }

Conventions

Conventions are responsible for converting field names of source object in appropriate field names of destination object.

For example, snake case convention means that field with name 'firstName' in source object will be mapped to 'first_name' field in destination object. Dee-mapper supports 3 types of conventions: camel case, pascal case and snake case.

You can create custom conventions using method registerConvention.

Source and destination types

You can use simple types and custom types for source and destination objects. There is only one simple type - Object. Custom types include all user defined types.

Type can be constructor function or class. The result of mapping depends on types that you used while configuring the mapping. Here are the rules (in format source type -> destination type) applied for types mapping:

  1. Simple -> Simple
  • all fields from source type will be converted and mapped to destination type by convention.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3'
});

console.log(result); //{ field_1: 'test1', field_2: 'test2', field_3: 'test3' }
console.log(result instanceof Object); // true
  1. Simple -> Custom
  • In a result there will be only converted fields from source value that are included in Custom type.
const sourceType = Object;
class DestinationType {
  constructor() {
    this.field_2 = undefined;
    this.field_3 = undefined;
  }
}

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, DestinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_2: 'test2', field_3: 'test3' }
console.log(result instanceof DestinationType); // true
  1. Custom -> Simple
  • only fields from source value that are included in source custom type will be converted and added to the result.
class SourceType {
  constructor() {
    this.field1 = undefined;
    this.field2 = undefined;
  }
}
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', SourceType, destinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_1: 'test1', field_2: 'test2' }
console.log(result instanceof Object); // true
  1. Custom -> Custom
  • only fields from source value that are included in source custom type will be converted. Converted fields will be added to the result only if they are included in destination type.
class SourceType {
  constructor() {
    this.field1 = undefined;
    this.field2 = undefined;
  }
}
class DestinationType {
  constructor() {
    this.field_2 = undefined;
    this.field_3 = undefined;
  }
}

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', SourceType, DestinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_2: 'test2' }
console.log(result instanceof DestinationType); // true

API

Mapper instance

mapper.register(conventionName, methodName, SourceType, DestType[, configCb])

Register new map for the mapper.

Args:

  • conventionName [String]: name of fields convention.
  • methodName [String]: name of method on mapper instance.
  • SourceType [Function]: type of source object.
  • DestType [Function]: type of destination object.
  • configCb [Function]: optional. Function for additional mapping configuration. Mapping instance is passed as a parameter.

mapper.generateType(typeName, fields)

Generates new custom type. Use the method as a helper if you don't want to write boilerplate code like:

function TypeName() {
    this.field1 = undefined;
    this.field2 = undefined;
}

// the same using mapper.generateType
const TypeName = mapper.generateType('TypeName', ['field1', 'field2']);

Args:

  • typeName [String]: type name.
  • fields Array([String]): array of fields that will be created for the type.

Returns: custom type function constructor.

mapper.registerConvention(conventionName, ConventionImplementation)

Register new field convention. See conventions for more details.

Args:

  • conventionName [String]: name of the convention.
  • ConventionImplementation [Function]: function constructor of the convention.

Object that is created with ConventionImplementation constructor should have method getField.

Example of upper case convention (each field name will be transformed to upper case field name):

class Convention {
    getField(sourceFieldName) {
        return sourceFieldName.toUpperCase();
    }
}

mapper.extendMap(methodName, implementation)

Extend mapping instance with custom method.

Args:

  • methodName [String]: name of the method.
  • implementation [Function]: function that takes source object as a parameter.

mapper.CAMEL_CASE_CONVENTION

Constant for camel case convention. You don't have to implement the convention manually.

mapper.PASCAL_CASE_CONVENTION

Constant for pascal case convention. You don't have to implement the convention manually.

mapper.SNAKE_CASE_CONVENTION

Constant for snake case convention. You don't have to implement the convention manually.

Mapping instance

mapping.mapField(destFieldName, sourceFieldNameOrCallback)

Map destination field on source field.

Args:

  • destFieldName [String]: destination object field name.
  • sourceFieldNameOrCallback [String|Function]: if string - name of source field that's used in mapping. If function - callback that executes with source value.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.mapField('field_1', 'field4')
    .mapField('field_2', value => value.field2.toUpperCase());
});

const result = mapper.testMap({
  field2: 'test2',
  field4: 'test4'
});

console.log(result); //{ field_1: 'test4', field_2: 'TEST2' }

mapping.mapFieldByPath(destFieldName, pathInSourceObj)

Map destination field on source field by path. (see get-value for path variants.)

Args:

  • destFieldName [String]: destination object field name.
  • pathInSourceObj [String]: path in source object.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.mapFieldByPath('field_1', 'field4')
    .mapFieldByPath('field_2', 'field2.name');
});

const result = mapper.testMap({
  field2: {
    name: 'test2'
  },
  field4: 'test4'
});

console.log(result); //{ field_1: 'test4', field_2: 'test2' }

mapping.ignoreField(destFieldName)

Ignore destination field. The field will be excluded from destination object.

Args:

  • destFieldName [String]: destination object field name.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.ignoreField('field_2');
});

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2'
});

console.log(result); //{ field_1: 'test1' }

mapping.convert(convertCb)

Use custom converter for mapping.

Args:

  • convertCb [Function]: function that makes mapping. The function takes single parameter - source value.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.convert((obj) => {
    return {
       newField1: obj.field1,
       newField2: obj.field2
    };
  });
});

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2'
});

console.log(result); //{ newField1: 'test1', newField2: 'test2' }

Async Mapping

To make mapping async you should pass name that ends with 'Async' in register method.

const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMapAsync', sourceType, destinationType, (map) => {
  map.convert((obj) => {
    return Promise.resolve({
       newField1: obj.field1,
       newField2: obj.field2
    });
  });
});

mapper.testMapAsync({
  field1: 'test1',
  field2: 'test2'
}).then((result) => {
  console.log(result); //{ newField1: 'test1', newField2: 'test2' }
});

What's in a name?

Dee is one of my favorite detective characters - Judge Dee.

Author

Ilya Markevich - @ilya_mark91