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

deepmerge-json

v1.5.0

Published

A library to deep merge json files with array operations

Downloads

86,908

Readme

Deepmerge Json

Build Status GitHub tag (latest SemVer) npm Snyk Vulnerabilities for GitHub Repo

What

A simple library to deep merge json files with array operations. Hugely inspired by the awesome deepmerge project.

Note that you can use to merge javascript objects, but the main reason this library was created was to merge json files with optional special operations for array merging.

Why

Sometimes you need a deeply copied objects. Sometimes you need to alter these objects to make them more extensible and generic, specially when you are using convention over configuration or convention over code patterns.

The objective of this library is to help you with these situations being really simple, performatic and small (~1.5kb).

How

let left = {
  keep: 'keep',
  simple: 10,
  obj: { a: { multi: { level: 'property' } }, is: 'ok' },
  merge: [0, 1, 2, 3],
  append: [0, 1, 2],
  prepend: [0, 1, 2]
};

let right = {
  simple: 20,
  obj: { a: { multi: { level: 'and' }, deep: 'property' } },
  merge: [10, 20],
  append: { $append: [3, 4, 5] },
  prepend: { $prepend: [-2, -1] }
};

let result = merge(left, right);
console.log(result);

// Result
{
  keep: 'keep',
  simple: 20,
  obj: { a: { multi: { level: 'and' }, deep: 'property' }, is: 'ok' },
  merge: [10, 20, 2, 3],
  append: [0, 1, 2, 3, 4, 5],
  prepend: [-2, -1, 0, 1, 2]
}

Explanation: it deeply merges right object into left without altering them and their properties. Everything is cloned. Arrays elements are merged based on right parameters (operators) passed as objects.

Installation

With NPM

npm install deepmerge-json

After that you can import it:

import merge from 'deepmerge-json';

or

const merge = require('deepmerge-json');

There is even a Typescript d.ts definition file to support auto complete.

With a CDN

Just add this line to your HTML file:

<script src="https://unpkg.com/deepmerge-json@latest/dist/deepmerge-json.min.js></script>

Usage

The main reason this library was created was to mimic and extend some array merging functions from mongodb when merging two sets of properties json files.

Simple Merge

It is possible to merge recursively all types of properties.

const left = {
  boolValue: false,
  numberValue: 100,
  stringValue: 'target',
  objectValue: {
    foo: 0,
    bar: 'bar',
    baz: {
      baz1: 1,
      baz2: 2
    }
  }
};

const right = {
  boolValue: true,
  numberValue: 222,
  stringValue: 'source',
  objectValue: {
    foo: 'foo',
    baz: {
      baz3: 3
    }
  }
};

const res = merge(left, right);

// result
{
  boolValue: true,
  numberValue: 222,
  stringValue: 'source',
  objectValue: {
    foo: 'foo',
    bar: 'bar',
    baz: {
      baz1: 1,
      baz2: 2,
      baz3: 3
    }
  }
}

Clone

You can clone an object omitting the second parameter. This will execute the merge function with an empty second parameter, which results in a deep clone of the first one.

Notice that if you pass anything other than undefined to the second parameter (even null), it will be actively used in the merge process.

You can also use the merge.clone() method which is an alias to the merge method with a single parameter. It's also more semantically meaningful.

Array Merge

Merging arrays are special because sometimes you want to append elements, sometimes prepend and sometimes you want to merge them.

Mongodb handles this nicely (IMHO). It has a $push property (among others) that let you append elements to an array when updating a document.

Inspired on that, this library has some merging methods (here called operations) to help you merge or improve the arrays from the original object. Just keep in mind that no matter the depth of the array, you just need to have the same path to the objects you want to merge.

Merging Elements

This is the default behavior. It merges the arrays elements one by one. It will add elements to the end if there more on the right than on the left element.

const left = {
  foo: [1, 2, 3],
  bar: []
};

const right = {
  foo: [10, 20],
  bar: [10, 20, 30]
};

const result = merge(left, right)

// Result
{
  foo: [10, 20, 3],
  bar: [10, 20, 30]
}

$push / $append

You can use the $push or $append operation to add elements to the end of the "left" array.

const left = [0, 1, 2];
const right = { $push: [3, 4, 5] };
const result = merge(left, right);

// Result
[0, 1, 2, 3, 4, 5];

$prepend

Similarly, you can use $prepend operation to add elements to the beginning of the "left" array.

const left = [0, 1, 2];
const right = { $prepend: [-2, -1] };
const result = merge(left, right);

// Result
[-2, -1, 0, 1, 2];

$set

Use $set when you want to completely replace "left" array by the "right" one.

const left = [0, 1, 2, 3, 4, 5, 6];
const right = { $set: [10, 20] };
const result = merge(left, right);

// Result
[10, 20];

$replace

Use $replace to replace or add indexed elements by their indexes. Indexes can be numbers or strings and cannot be less than 0 or NaN values.

With valid indexes:

const left = [10, 20, 30];
const right = { $replace: { 0: 100, 2: 300, 4: 400 } };
const result = merge(left, right);

// Result (note that the element with index 3 was never given)
[100, 20, 300, , 400];

With invalid indexes:

const left = [10, 20, 30];
const right = { $replace: { null: 0, foo: 0, true: 0, '-1': 0 } };
const result = merge(left, right);

// throws an Error

With objects

[1.4.0] It completely replaces the indicated left element with the corresponding right element.

Note for users of version < 1.4.0: if you want the old operation functionality, replace $replace for $merge.

const left = [{ a: 1, b: 1 }, { c: 1 }];
const right = { $merge: [{ a: 2 }] };
const result = merge(left, right);

// Result
[{ a: 2 }, { c: 1 }];

$merge

Use $merge to merge or add indexed elements by their indexes. Indexes can be numbers or strings and cannot be less than 0 or NaN values. It's similar to $replace but instead of replacing the values when found, it merges them with the new values.

With valid indexes:

const left = [10, 20, 30];
const right = { $merge: { 0: 100, 2: 300, 4: 400 } };
const result = merge(left, right);

// Result (note that the element with index 3 was never given)
[100, 20, 300, , 400];

With invalid indexes:

const left = [10, 20, 30];
const right = { $merge: { null: 0, foo: 0, true: 0, '-1': 0 } };
const result = merge(left, right);

// throws an Error

With objects

const left = [{ a: 1, b: 1 }, { c: 1 }];
const right = { $merge: [{ a: 2 }] };
const result = merge(left, right);

// Result
[{ a: 2, b: 1 }, { c: 1 }];

$insert

Use $insert to insert indexed elements at their indexes. Indexes can be numbers or strings and cannot NaN values. Notice that elements change places as you insert them. Negative numbers insert them to the end of the array. See Array.splice.

With positive indexes:

const left = [10, 20, 30];
const right = { $insert: { 0: 100, 2: 200, 10: 400 } };
const result = merge(left, right);

// Result (notice that the elements moved and the 400 was added to the last index)
[100, 10, 200, 20, 30, 400];

With negative indexes:

const left = [10, 20, 30];
const right = { $insert: { '-1': 100, '-2': 200, '-10': 0 } };
const result = merge(left, right);

// Result
[0, 10, 20, 200, 100, 30];

With invalid indexes:

const left = [10, 20, 30];
const right = { $insert: { null: 100, foo: 300, true: 400 } };
const result = merge(left, right);

// throws an Error

Skipping Elements

If you skip some elements in the "right" array, the respective "left" elements will be kept in the result. This is not very useful for json merging since it's ot possible to create a sparse array per se, but it's a nice consequence of the merge method.

const left = [1, 20, 3, 40, 5, 60];
const right = [10, , 30, , 50];
const result = merge(left, right);

// Result
[10, 20, 30, 40, 50, 60];

Multiple Operations

Starting from version 1.3.0 it's possible to use multiple operations at once. They are executed in place and in order.

const left = [2, 3, 4];
const right: {
  $prepend: [0, 1],
  $append: [5, 6],
  $replace: { 0: 100 };
}
const result = merge(left, right);

// Result
[100, 1, 2, 3, 4, 5, 6]
const left = [2, 3, 4];
const right: {
  $replace: { 0: 100 };
  $prepend: [0, 1],
  $append: [5, 6],
}
const result = merge(left, right);

// Result
[0, 1, 100, 3, 4, 5, 6]

Merging Multiple Objects

You can also merge multiple objects with the help of the utility method merge.multi(). It respects the order of the parameters and the operations just like expected if you call merge multiple times passing the last result as the first parameter to the next call.

const obj1 = { a: 0, b: [true, { c: 'ok' }] };
const obj2 = { a: 10, d: false };
const obj3 = { a: 20, b: { $push: [42] } };

const result = merge.multi(obj1, obj2, obj3);

// Result
{ a: 20, b: [true, { c: 'ok' }, 42], d: false }

Options

For now, no options yet :chipmunk:.

Contributing

If you are nice enough you can submit bugs and features to the issue board and make this lib great and useful for you and the community.

But if you are really nice you can submit a PR and make this lib awesome!

Rough Performance Test

Just a fun performance test with a 1 million runs. I'm not a performance expert so they might not be very precise.

Testing machine:

  • CPU: Intel Core i5-9300H @ 2.4GHz x8
  • Memory: 32GB
  • SO: Ubuntu 20.04.4 LTS

| Measures | Node 17.7.2 | Chrome 100.0.4896.75 | Firefox 99.0 2 :thinking: | | ------------------- | ----------- | -------------------- | ------------------------------------ | | Max. Value | 279,763.93 | 295,386.07 | 852,514.92 | | Min. Value | 277,344.35 | 287,802.91 | 827,814.57 | | Average1 | 279,574.05 | 293,929.66 | 840,884.13 |

  • Operations per second
  • 1 The average is calculated removing the maximum and the minimum values
  • 2 For some reason Firefox returned a really good but suspicious performance