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

deeply

v3.1.0

Published

A toolkit for deep structure manipulations, provides deep merge/clone functionality out of the box, and exposes hooks and custom adapters for more control and greater flexibility.

Downloads

4,718

Readme

Deeply NPM Module

A toolkit for deep structure manipulations, provides deep merge/clone functionality out of the box, and exposes hooks and custom adapters for more control and greater flexibility.

PhantomJS Build Linux Build MacOS Build Windows Build

Coverage Status Dependency Status MIT license

Readme

| compression | size | | :--------------- | -------: | | deeply.js | 16.35 kB | | deeply.min.js | 5.36 kB | | deeply.min.js.gz | 1.63 kB |

Table of Contents

Install

$ npm install deeply --save

Examples

By default it provides interface for immutable operations, also available via explicit require require('deeply/immutable') or require('deeply').immutable property.

Merging

Deeply merges two or more objects.

var merge = require('deeply');

var result = merge({a: {a1: 1}}, {a: {a2: 2}}, {b: {b3: 3}});

assert.equal(result, {a: {a1: 1, a2: 2}, b: {b3: 3}});

Security concerns

Due to Prototype Pollution security vulnerability concerns, default behavior of when merging objects is to skip unsafe keys, like __proto__, please refer to the test/compatability.js file for code examples.

If there is a use case where such behavior is desired, pass allowDangerousObjectKeys flag to the context to skip keys safety checks.

var merge = require('deeply');
var result;

var context = { allowDangerousObjectKeys: merge.behaviors.allowDangerousObjectKeys };

result = merge.call(context, {}, JSON.parse('{"__proto__": {"a0": true}}'));
// end of the world, cats live with dogs...

Cloning

As degenerated case of merging one object on itself, it's possible to use deeply as deep clone function.

var merge = require('deeply');
var clone = merge;

var x = {a: {b: {c: 1}}};
var y = clone(x);

y.a.b.c = 2;

assert.equal(x.a.b.c, 1);

Arrays Custom Merging

By default array treated as primitive values and being replaced upon conflict, for more meaningful array merge strategy, provide one of the pre-built array merge helpers or a custom reduce function within invocation context.

Default Behavior

var merge = require('deeply');
var result = merge({ a: { b: [0, 2, 4, {a: 'A'}], c: 'first' }}, { a: {b: [1, 3, 5, {b: 'B'}], d: 'second' }});

assert.equal(result, { a: { b: [1, 3, 5, {b: 'B'}], c: 'first', d: 'second' }});

Combining Arrays

var merge = require('deeply');
var result;

var context =
{
  useCustomAdapters: merge.behaviors.useCustomAdapters,
  'array'          : merge.adapters.arraysCombine
};

// it might be useful when you have array of objects
result = merge.call(context, { a: { b: [0, {a: 'A1', b: 'B1'}, 4] }}, { a: {b: [1, {a: 'A2', c: 'C2'}, 5] }});
assert.equal(result, { a: { b: [1, {a: 'A2', b: 'B1', c: 'C2'}, 5] }});

Appending Arrays

var merge = require('deeply');

var context =
{
  useCustomAdapters: merge.behaviors.useCustomAdapters,
  'array'          : merge.adapters.arraysAppend
};

var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});

assert.equal(result, { a: { b: [0, 2, 4, 4, 2, 0, 1, 3, 5, 5, 3, 1] }});

Appending Arrays and Keeping Unique Elements Only

var merge = require('deeply');

var context =
{
  useCustomAdapters: merge.behaviors.useCustomAdapters,
  'array'          : merge.adapters.arraysAppendUnique
};

var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});

assert.equal(result, { a: { b: [0, 2, 4, 1, 3, 5] }});

Custom Merge Function

For example we need to have merging arrays to be appended, with only unique elements and sort the result array.

var merge = require('deeply');

var context =
{
  useCustomAdapters: merge.behaviors.useCustomAdapters,
  'array'          : appendUniqueAndSort
};

var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }});

assert.equal(result, { a: { b: [0, 1, 2, 3, 4, 5] }});

// Custom array adapter
function appendUniqueAndSort(to, from, merge)
{
  // append only if new element isn't present yet
  from.forEach(function(v) { to.indexOf(v) == -1 && to.push(merge(undefined, v)); });

  // and sort
  return to.sort();
}

Cloning Functions

By default, functions copied as is to the result object, basically being treated as primitive values.

You can use functionsClone adapter to clone functions, creating new function object with the same signature as original.

var clone = require('deeply');

function subj(a, b)
{
  return a + b + 10;
}

subj.customProp = 13;

subj.prototype.A = 1;

subj.prototype.B = {isB: 'true'};

var context =
{
  useCustomAdapters: clone.behaviors.useCustomAdapters,
  'function'       : clone.adapters.functionsClone
};

var result = clone.call(context, { a: { b: subj}});

// cloned object function named subj
assert.equal(result, { a: { b: subj}});

// same signature
assert.equal(subj.name, result.a.b.name);
assert.equal(subj.length, result.a.b.length);

assert.equal(subj.customProp, result.a.b.customProp);

// separate objects
subj.isOriginal = true;
result.a.b.isCopy = true;

assert.equal(subj.isOriginal, true);
assert.equal(subj.isCopy, undefined);

assert.equal(result.a.b.isOriginal, undefined);
assert.equal(result.a.b.isCopy, true);

// same output
assert.equal(subj(3, 4), result.a.b(3, 4));

Cloning Prototype Chain

It will also clone prototype objects, so use this option with caution.

var clone = require('deeply');

function Subj()
{
  this.boom = 'Zap!';
}

Subj.prototype.A = 1;

Subj.prototype.B = {isB: 'true'};

var context =
{
  useCustomAdapters: clone.behaviors.useCustomAdapters,
  'function'       : clone.adapters.functionsClone
};

var result = clone.call(context, { class: Subj });

// cloned object function named Subj
assert.equal(result, { class: Subj });

// has prototype properties
assert.equal(result.class.prototype.A, 1);
assert.equal(result.class.prototype.B.isB, 'true');

// prototypes are decoupled
Subj.prototype.C = 2;
assert.equal(Subj.prototype.C, 2);
assert.equal(result.class.prototype.C, undefined);

// instances
var s1 = new Subj();
var s2 = new result.class();

assert.equal(s1.A, s2.A);
assert.equal(s1.B, s2.B);

assert.equal(s1.C, 2);
assert.equal(s2.C, undefined);

assert.equal(s1.boom, s2.boom);

// but reported instanceof isn't the same
assert.equal(s1 instanceof Subj, true);
assert.equal(s2 instanceof Subj, false);

Extend Original Function Prototype

When having proper instanceof results matters, you can use functionsExtend helper instead.

var clone = require('deeply');

function Subj()
{
  this.boom = 'Zap!';
  return this.boom;
}

Subj.customProp = 13;

Subj.prototype.A = 1;

Subj.prototype.B = {isB: 'true'};

var context =
{
  useCustomAdapters: clone.behaviors.useCustomAdapters,
  'function'       : clone.adapters.functionsExtend
};

var result = clone.call(context, { class: Subj });

// cloned object function named Subj
assert.equal(result, { class: Subj });

// same signature
assert.equal(Subj.name, result.class.name);
assert.equal(Subj.length, result.class.length);

assert.equal(Subj.customProp, result.class.customProp);

// separate objects
Subj.isOriginal = true;
result.class.isCopy = true;

assert.equal(Subj.isOriginal, true);
assert.equal(Subj.isCopy, undefined);

assert.equal(result.class.isOriginal, undefined);
assert.equal(result.class.isCopy, true);

// same output
assert.equal(Subj(), result.class());

// has prototype properties
assert.equal(result.class.prototype.A, 1);
assert.equal(result.class.prototype.B.isB, 'true');

// prototypes are extended
Subj.prototype.X = 67;
assert.equal(Subj.prototype.X, 67);
assert.equal(result.class.prototype.X, 67);

// instances
var s1 = new Subj();
var s2 = new result.class();

assert.equal(s1.A, s2.A);
assert.equal(s1.B, s2.B);

assert.equal(s1.boom, s2.boom);

// but reported instanceof isn't the same
assert.equal(s1 instanceof Subj, true);
assert.equal(s2 instanceof Subj, true);

Custom flags and hooks

allowDangerousObjectKeys

As shown in (Security Concerns)[#security-concerns] section, you can skip safety checks for unsafe object keys (e.g. __proto__) by passing allowDangerousObjectKeys flag to the context.

merge.call({ allowDangerousObjectKeys: merge.behaviors.allowDangerousObjectKeys }, {}, JSON.parse('{"__proto__": {"a0": true}}'));

useCustomAdapters

As shown in Custom Merge Function example, you can add custom adapters for any data type that supported by precise-typeof.

For this example we will combine arrays of number, by performing addition operation on array elements.

var merge = require('deeply');

var context =
{
  useCustomAdapters: merge.behaviors.useCustomAdapters,
  'array'          : merge.adapters.arraysCombine,
  'number'         : addNumbers
};

var result = merge.call(context, { a: { b: [0, 2, 4, 4, 2, 0] }}, { a: {b: [1, 3, 5, 5, 3, 1] }}, { a: {b: [7, 8, 9, 10, 11, 12] }});

assert.equal(result, { a: { b: [8, 13, 18, 19, 16, 13] }});

// Custom number adapter
function addNumbers(to, from)
{
  return (to || 0) + from;
}

useCustomTypeOf

In some cases you might need to have more control over type detection, for that you can supply your own type detection function.

In following example we'd use same precise-typeof library, but with pojoOnly: true flag:

var merge = require('deeply');
var typeOf = require('precise-typeof');
var moment = require('moment');

var context =
{
  useCustomTypeOf: merge.behaviors.useCustomTypeOf,
  'typeof'       : (input) => typeOf(input, {pojoOnly: true})
};

var result = merge.call(context, { a: {someField: 'value'}, b: 'other thing'}, { a: moment.utc('2018-11-27') });

assert.equal(result, { a: moment.utc('2018-11-27'), b: 'other thing' });

In the above example, it would treat moment object as atomic, and won't mix it's properties with other properties.

Mutable Operations

Mutable interface supports all the described operations, and available via explicit require require('deeply/mutable') or require('deeply').mutable property.

var merge = require('deeply/mutable');
var myObj = {a: {a1: 1, a2: 2}, b: {b1: 11, b2: 12}};

merge(myObj, {c: 'c', d: 'd'}, {x: {y: {z: -Infinity}}});

assert.equal(myObj, {a: {a1: 1, a2: 2}, b: {b1: 11, b2: 12}, c: 'c', d: 'd', x: {y: {z: -Infinity}}});

Ludicrous Mode

Also as shortcut and a homage to Tesla, ludicrous mode is available, that will clone functions and it's prototype objects by default. :) Details could be found in Cloning Functions examples.

var ludicrous = require('deeply/ludicrous');
var scopeVar = 6;

function original(a, b)
{
  return a + b + scopeVar;
}

var cloned = ludicrous({ func: original });

// cloned object function named subj
assert.equal(cloned, { func: original });

// same signature
assert.equal(original.name, cloned.func.name);
assert.equal(original.length, cloned.func.length);

// separate objects
original.isOriginal = true;
cloned.func.isCopy = true;

assert.equal(original.isOriginal, true);
assert.equal(original.isCopy, undefined);

assert.equal(cloned.func.isOriginal, undefined);
assert.equal(cloned.func.isCopy, true);

// same output
assert.equal(original(1, 2), cloned.func(1, 2));

Note: ludicrous isn't included into the main deeply package, so it won't be automatically pulled in, if you're bundling using browserify deeply/index.js, to use ludicrous in the browser you'd need to explicitly require it in your modules or specify direct path in your bundler config.

Want to Know More?

More examples can be found in test/compatability.js.

Or open an issue with questions and/or suggestions.

License

Deeply is released under the MIT license.