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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nomadplanit/fast-transform

v1.0.3

Published

A library for transforming JSON in Node.js.

Downloads

4

Readme

fast-node-transform

This module will allow you to perform JSON transformations using a template driven approach. To make the transformation fast, the template is compiled into a single function much like HTML templating libraries.

Installation

npm install --save @nomad-planit/fast-transform

Basic Usage

The library uses a compiler to transform a template into an object that can be used to quickly transform a JavaScript object into another object. The template is a familiar JSON-like construct that respresents the destination keys and the source values. A template is compiled into a function that takes the source object as a parameter and will return the transformed object.

const transformer = require('@nomadplanit/fast-transform');
const transform = transformer.compile({
    id: 'userId',
    name: 'firstName'
});
const newObject = transform({
    userId: '1',
    firstName: 'Jane',
    lastName: 'Doe'
});
/*
  newObject = {
    id: '1',
    name: 'Jane'
  }
*/

The template takes the form of shaping your resulting data structure by creating a JavaScript object that contains the destination fields in the left hand side and the source fields on the right hand side. A simple template would look something like this:

{
  id: 'userId',
  name: 'firstName'
}

This template is going to transform userId into id and firstName into name. So given a structure like:

{
  userId: '123',
  firstName: 'Jane',
  lastName: 'Doe',
  address: {
    street: '1 Main St',
    city: 'San Jose',
    state: 'California',
    zip: '95013'
  }
}

you would get this result:

{
  id: '123,
  name: 'Jane'
}

You can also set the right side value as null if source and destination have the same field name. So a template like this:

{
  id: 'userId',
  name: 'firstName',
  address: null
}

will result in a structure like this:

{
  id: '123,
  name: 'Jane',
  address: {
    street: '1 Main St',
    city: 'San Jose',
    state: 'California',
    zip: '95013'
  }
}

Field Name Rules

The field names have the flexibility of indicating nesting inside the field name itself. So if the source comes from a nested field, you can indicate this with object notation format. So for example, if we wanted to put the street into the root object we could create a mapping like this:

{
  street: 'address.street'
}

The same rule applies to destination path as well. If you wanted to put name into an object with field name personal, we could write a mapping like this:

{
  'personal.name': 'name'
}

The field level source and destination code is using lodash get and set. So the same rules that apply to lodash, apply to field mappings.

In the case of indicating nesting in the destination, if the source object already exists, the field in that object will simply be set to the transformed value. If the object does not exist, one will be created before setting the field's value.

Literal String Values

String literals can be used in place of the mapping value. To use a literal value prefix the mapping value (RHS) with a tilde (~).

{
  literal: '~value'
}

This will result in the literal field just having value as its value.

Destination Arrays

You can transform values and push them into an array by adding opening and closing brackets onto the end of the destination field mapping name. This will create an array and push the transformed value into the array. For example:

{
  `colors[]`: 'car.color`,
  `colors[]`: 'bike.color`,
  `colors[]`: 'house.color`
}

given:

{
  car: {
    color: 'red'
  },
  bike: {
    color: 'blue'
  },
  house: {
    color: 'white'
  }
}

would result in:

{
  colors: [
    'red',
    'blue',
    'white'
  ]
}

Embedded Documents

You can also created embedded document schemas to create nested objects. This has a special format that requires an object on the right hand side to describe the source object to transform as well as the schema to use for the transformation. For example, maybe we need our address structure to look different than the one being provided. To do this we could create a transformation template that looks like this:

{
  id: 'userId',
  name: 'firstName',
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      postalCode: 'zip
    }
  }
}

Given the example for Jane Doe above as input we would get a result like this:

{
  id: '123,
  name: 'Jane',
  address: {
    deliveryLine: '1 Main St',
    city: 'San Jose',
    state: 'California',
    postalCode: '95013'
  }
}

There can be any number of embedded documents. The embedded schemas are treated as separate transformation templates and just follow the same rules as a top level transformation template. The only thing to keep in mind is whatever object resolves into the provided srcPath will be the object that is used in the transformation. So the right hand side fields need to be part of the source object provided.

Custom Filter Functions

You can also provide a function to use in the transformation for a field. This lets you do special formatting or more complex transformation that might not be able to be done using a standard field mapping. For example, what if we also wanted to store the state abbreviation as the stateCode field. Given a map of the state name to abbreviation we could write a custom filter like this:

{
  id: 'userId',
  name: 'firstName',
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      stateCode: {
        srcPath: 'state',
        filter: (value) => {
          return _stateLookup.get(value.toLowerCase();
        }
      }
      postalCode: 'zip
    }
  }
}

and we would end up with an object like this:

{
  id: '123,
  name: 'Jane',
  address: {
    deliveryLine: '1 Main St',
    city: 'San Jose',
    state: 'California',
    stateCode: 'CA',
    postalCode: '95013'
  }
}

The function signature for custom filters like done in the example above are as follows:

function (value, src, dst, data, parent, top) {}

Parameter | Type | Description --- | --- | --- value | * | The is the value that resolves to the srcPath in the field mapping that contains the custom filter. src | object | This is the source object in the scope of the current transformation template. Remember embedded templates will be scoped to the source indicated in the srcPath of the embedded template. dst | object | This is the destination object being built. In the case of embedded template, this will be the nested object. data | * | When executing transformations it is possible to pass custom data into the transformation. This field will contain that data. parent | object | When transforming an embedded template, this field will contain the direct parent to the nested object being transformed. top | object | When transforming nested object, this will be the top most or original object being transformed.

The return value from this function should be the value you wish to set in the destination object.

Alternative Custom Filter

An alternative way to provide a custom filter is by setting the right hand side to a function. This will result in executing the function with a little different signature. Since a srcPath isn't being provided, there will be no value. The signature looks the same as previous described without the value parameter. As an example we could use a function like this to transform first and last name into a full name:

{
  id: 'userId',
  name: (src) => {
    return `${src.firstName} ${src.lastName}`,
  },
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      stateCode: {
        srcPath: 'state',
        filter: (value) => {
          return _stateLookup.get(value.toLowerCase();
        }
      }
      postalCode: 'zip
    }
  }
}

will net this object:

{
  id: '123,
  name: 'Jane Doe',
  address: {
    deliveryLine: '1 Main St',
    city: 'San Jose',
    state: 'California',
    stateCode: 'CA',
    postalCode: '95013'
  }
}

Special srcPath Values

To make transformation of nested objects easier there are some special values that can be used in the srcPath for embedded documents.

value | description --- | --- $self | This will cause the transformation source object to remain the same as what is currently being processed. So the destination object will nest but the source object will not. $parent | This will result in the transformation source object to be set as the parent of the currently nested source object. So in the example above if you were creating a nested object in address but the values come from the root object, you could set the srcPath to $parent to achieve this. $top | This will set the transformation source object as the root or top level object being transformed.

As an example, maybe the coordinates for Jane Doe's address is in the top level but we want to put this into the address. Our template could look like this:

{
  id: 'userId',
  name: (src) => {
    return `${src.firstName} ${src.lastName}`,
  },
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      stateCode: {
        srcPath: 'state',
        filter: (value) => {
          return _stateLookup.get(value.toLowerCase();
        }
      }
      postalCode: 'zip,
      coordinates: {
        srcPath: '$parent',
        schema: {
          lat: 'location.latitude',
          lon: 'location.longitude'
        }
      }
    }
  }
}

Given an object like this:

{
  userId: '123',
  firstName: 'Jane',
  lastName: 'Doe',
  address: {
    street: '1 Main St',
    city: 'San Jose',
    state: 'California',
    zip: '95013'
  },
  location: {
    latitude: 123.12345,
    longitued: 85.89012
  }
}

we would get an object like this:

{
  id: '123,
  name: 'Jane Doe',
  address: {
    deliveryLine: '1 Main St',
    city: 'San Jose',
    state: 'California',
    stateCode: 'CA',
    postalCode: '95013',
    coordinates: {
      lat: 123.12345,
      lon: 85.89012
  }
}

Special Object Specifiers In Mapping Field Names

You can also use the special object specifiers above directly in the source field mapping names. So the above example could also be written like this:

{
  id: 'userId',
  name: (src) => {
    return `${src.firstName} ${src.lastName}`,
  },
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      stateCode: {
        srcPath: 'state',
        filter: (value) => {
          return _stateLookup.get(value.toLowerCase();
        }
      }
      postalCode: 'zip,
      'coordinates.lat': '$parent.location.latitude',
      'coordinates.lon': '$parent.location.longitude'
    }
  }
}

The result is the same but the above template is not as efficient as the previous one due to complexity in the field names. This is OK in small doses but for large object transformations it is better to use the embedded schemas.

Array of Objects In The Source

For embeded schemas you can also transform arrays of objects. If the srcPath resolves into and array of objects, each object in the array will be transformed according to the provided schema and the resulting array of transformed objects will be set into the destination field.

One thing to keep in mind is if the destination field already contains an array, the value in will be replaced like any other transformation. Conversely, if you are transforming a single object into a field that contains an array, the object will be added to the end of the array.

Order Matters

The schema is ultimately compiled into an array of functions that get executed in order. So the order of the fields in the template make a difference as to when fields in the destination structure will exist. It is possible to use this to your advantage. As an example we will change the transformation above to use field level nesting and the knowledge that the address structure will exist by the time the coordinates are transformed:

{
  id: 'userId',
  name: (src) => {
    return `${src.firstName} ${src.lastName}`,
  },
  address: {
    srcPath: 'address',
    schema: {
      deliveryLine: 'street',
      city: null,
      state: null,
      stateCode: {
        srcPath: 'state',
        filter: (value) => {
          return _stateLookup.get(value.toLowerCase();
        }
      }
      postalCode: 'zip,
    }
  },
  'address.coordinates.lat': 'location.latitude',
  'address.coordinates.lon': 'location.longitude'
}

This will result in the same structure but using a different mechanism for describing the transformation:

{
  id: '123,
  name: 'Jane Doe',
  address: {
    deliveryLine: '1 Main St',
    city: 'San Jose',
    state: 'California',
    stateCode: 'CA',
    postalCode: '95013',
    coordinates: {
      lat: 123.12345,
      lon: 85.89012
  }
}

Again, this is not a very practical example and is still inefficient, but it gives you the idea about how this could be achieved.