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

@gynzy/mapper

v0.1.8

Published

Convention based object mapper inspired by AutoMapper

Downloads

18

Readme

mapper

Build Status Coverage Status

mapper is a zero-dependency nodejs library solving one of the most tedious developer tasks - mapping an object to another type of object. Object mapping works by transforming a source object of one type to another type. Often objects have similar fields and it's tedious and error-prone to copy each individually. With mapper these fields will be copied without any configuration.

How to use

After adding the library, you first need to create a mapping between the source and destination type with Mapper.createMap. This method returns a MappingBuilder allowing customizing the mapping (see below). Mapping objects can be done by one of the overloads of Mapper.map.

See also the tests in mapper.spec.ts, below a small summary is given.

import { Mapper } from '@gynzy/mapper';

class User {
    constructor(
        public readonly firstName: string,
        public readonly lastName: string,
        private readonly email: string,
    ) { }
}

class Person {
    constructor(
        public readonly firstName: string,
        public readonly lastName: string,
    ) { }
}

// Creates a mapping between User -> Person.
Mapper.createMap(User, Person);

// Maps user to person by instantiating a new Person object.
const sourceUser = new User('John', 'Denver', '[email protected]');
let person = Mapper.map(sourceUser, Person);

// Mapping to an existing object overwrites the fields.
// The object emma itself is modified and also returned.
const emma = new Person('Emma', 'Watson');
person = Mapper.map(sourceUser, emma);

// Maps to existing object by explicitly specifying the typ of destination object.
// The type of object remains anonymous (we can't actually change the type) but it is mapped with
// the fields Person.
const john = { firstName: 'John' } as any;
person = Mapper.map(sourceUser, john, Person);

// If source object is array, each element is mapped and an array is returned.
let persons = Mapper.map([
    new User('John', 'Denver', '[email protected]'),
    new User('Emma', 'Watson', '[email protected]'),
], Person);

Custom configuration

With the return object of Mapper.createMap, you can create a custom mapping behavior for each field. For instance, you can skip certain fields, map a field from a different field or use a custom factory to supply the value.

class Record {
    constructor(
        public readonly organizationId: number,
        public readonly userId: number,
        public readonly name: string,
        public readonly lastName: string,
    ) { }
}

class User {
    constructor(
        public readonly id: number,
        public readonly fullName: string,
        public readonly organization: Organization,
    ) { }
}

class Organization {
    constructor(
        public readonly id: number,
        public readonly name: string,
    ) { }
}

Mapper.createMap(Record, User)
    // map from different field
    .for('id').mapFrom('userId')
    // custom factory to construct value
    .for('organization').mapFrom((src) => Mapper.map(src, Organization))
    // factory supplies source and destination object
    .for('fullName').mapFrom((src, dst) => dst.fullName ?? `${src.name} ${src.lastName}`);

Mapper.createMap(Record, Organization)
    // map from different field
    .for('id').mapFrom('organizationId')
    // name refers to User
    .for('name').ignore();

forAll

Sometimes you have many fields for which you want to have the same configuration. For instance, when updating an object with fields from another object which uses the same names.

Mapper.createMap(Record, Organization)
    // Ignore all fields
    .forAll().ignore()
    // But do map id from organizationId
    .for('id').mapFrom('organizationId');

How it works

Mapping is done by listing all fields after instantiating a new instance of destination type. For each destination field, the value is resolved from the source, depending on the applied configuration. These include (see also examples above):

  • No configuration, value is copied from source object with same field name.
  • ignore() - destination value is untouched. Meaning, if an existing destination object is given, the value remains. If a new instance is created, the value is undefined.
  • constant(value: unknown) - provide constant value.
  • mapFrom(sourceMember: M) - map value from other source field (i.e. different name).
  • mapFrom(factory: (source, destination) => unknown) - resolve value with a custom factory. Source refers to the source object and destination to the destination object. Note: currently, no order of mapping execution is guaranteed, mapping based on a destination field, may give unexpected results.

Note: all expected fields must be set after instantiating the destination object, otherwise it is impossible to know which fields to map. See below for valid examples.

class Person {
    // All fields are set in the constructor
    constructor(
        public readonly firstName: string,
        public readonly lastName: string,
    ) { }
}

class Person {
    // Alternative, provide value if field is set as property
    public readonly firstName: string = undefined;
    public readonly lastName: string = undefined;

    // This field won't be mapped since no default value is set on instantion.
    public readonly unmapped: string;
}