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

two-way-mapper

v0.9.0

Published

Converts JavaScript objects from one format to another.

Downloads

5

Readme

two-way-mapper

Converts JavaScript objects from one format to another.

Supports asymmetric mapping.

Simple, powerful, composable, strongly-typed.

Overview

Quick Start

Install via npm install two-way-mapper

Import via import { mapper } from 'two-way-mapper'

All methods on the mapper namespace return a Mapper, which is an interface as follows:

interface Mapper<TLeft, TRight> { 
    map(input: TLeft): TRight;
    reverse(input: TRight): TLeft;
}

As you can see, the map method converts from TLeft to TRight. The reverse method converts from TRight to TLeft.

Primary Methods

mapper.properties(propertyMappers) - maps objects with similar properties

Let's convert between two similar User types:

const userA: UserA = { name: "Scott", active: "yes" };
const userB: UserB = { name: "Scott", active: true };

With mapper.properties, we supply a Mapper for each property:

import { mapper } from "two-way-mapper";

const userMapper = mapper.properties<UserA, UserB>({
  // Properties with the same name & type can simply be copied:
  name: mapper.copy,
  
  // Properties with different types can be mapped:
  active: mapper.convert(
    (active) => active === "yes",
    (active) => (active ? "yes" : "no")
  )
});

And now userMapper converts data from UserA to UserB:

// The `map` function takes UserA and returns UserB:
const userB = userMapper.map(userA);
// Likewise, the `reverse` function takes UserB and returns UserA:
const userA = userMapper.reverse(userB);

mapper.twoWay(leftMapper, rightMapper) - maps objects with different properties

Let's convert between two different User types:

const userA: UserA = { fullName: "Scott Rippey", status: 'active' };
const userB: UserB = { name: "Scott Rippey", active: boolean };

With mapper.twoWay, we use two mapping objects, for mapping properties in both directions:

const userMapper =  mapper.twoWay<UserA, UserB>(
  { 
    name: (input) => input.fullName, 
    active: (input) => input.status === 'active',
  }, 
  {
    fullName: (input) => input.name,
    status: (input) => input.active ? 'active' : 'inactive',
  }
)

And now, use userMapper.map and .reverse to map between UserA to UserB.

mapper.oneWay(leftMapper) - maps objects in one direction only

Sometimes, we only want to convert data in one direction.
A one-way mapper only converts from A to B, but not from B to A.

const userA: UserA = { friends: [ { ... }, { ... }, { ... } ] };
const userB: UserB = { friendsCount: 3 };

const userMapper = mapper.oneWay<UserA, UserB>({
  friendsCount: (input) => input.friends.length
});

The mapper still implements the two-way Mapper interface, but the reverse method does nothing.

oneWayReverse<A, B>() works the same as oneWay, but it only implements the reverse method that converts from B to A (and the map method does nothing).

mapper.combine(...mappers) - combines multiple mappers into a single mapper

Now, let's convert between two User types that have a mix of similar and different properties.

const userA: UserA = { id: 5, name: "Scott", status: "active" };
const userB: UserB = { id: 5, name: "Scott", active: true };

We can use mapper.combine to use both mapper.properties and mapper.twoWay in the same mapper:

const userMapper = mapper.combine(
  mapper.properties<UserA, UserB>({
    id: mapper.copy,
    name: mapper.copy,
  }),
  mapper.twoWay<UserA, UserB>({
    active: (input) => input.status === "active",
  }, {
    status: (input) => input.active ? "active" : "inactive",
  })
);

Utilities

The mapper namespace also contains several utilities to make type-safe mapping easier.

mapper.array(itemMapper) - maps all values in an array

mapper.copy - copies a value as-is

You can also use these strongly-typed variants:

  • mapper.copyAs<T>()
  • mapper.cast<TLeft, TRight>()

mapper.convert(left, right) - manually specify how to convert a value

mapper.constant(rightValue, leftValue) - use constant values when mapping

Utilities Example

const userMapper = mapper.properties<UserA, UserB>({
  // Converts all items in the array using itemMapper:
  items: mapper.array(itemMapper),
  
  // Copy the value as-is:
  id: mapper.copy,
  // Same as copy, but ensures both types are strings:
  name: mapper.copyAs<string>(),
  // Cast between two types (value is still copied as-is):
  status: mapper.cast<StatusEnumA, StatusEnumB>(),
  
  // Convert types manually:
  active: mapper.convert(
    input => input.active ? 'yes' : 'no',
    input => input.active === 'yes'      
  ),
  
  // Use a constant value when mapping:
  typename: mapper.constant('UserA', 'UserB'),
});