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

smart-extend

v1.7.4

Published

Merge/extend objects (shallow/deep) with global/individual filters and more features

Downloads

135

Readme

smart-extend

Build Status Coverage Code Climate NPM NPM

smart-extend is an extension to jQuery's classic extend() method with additional features providing you with more power and control over your object extensions/clones. Works in both Node.JS and the browser.

Highlights

  • Deep/shallow object cloning/extension.
  • Optional array concatination.
  • Optionally copy only 'own' properties.
  • Specify specific properties to copy.
  • Apply filter functions to allow only specific properties/values to be copied.
  • Expressive API.
  • Clean, Focused, and actively maintained.

Table of Contents

Example Usage

var extend = require('smart-extend');
var objA = {a:1, b:2};
var objB = {b:3, c:4};
var objC = {a:1, b:2, inner:{a:1, b:2}};
var objD = {b:3, c:4, inner:{b:3, c:4}};
var emptyObj = {};

// Copy objA into emptyObj
extend(emptyObj, objA)          //-> emptyObj === {a:1, b:2} 

// Copy objA & objB into emptyObj
extend(emptyObj, objA, objB)    //-> emptyObj === {a:1, b:3, c:4} 

// Shallow Copy objC & objD into a new object
extend({}, objC, objD)          //-> {a:1, b:3, c:4, inner:{b:3, c:4}} 

// Deep Copy objC & objD into a new object
extend.deep({}, objC, objD)     //-> {a:1, b:3, c:4, inner:{a:1, b:3, c:4}} 

// Clone objA (without specifying a target)
extend.clone(objA)              //-> {a:1, b:2}

// Clone objA with only property 'a'
extend.clone.keys(['a'])(objA)  //-> {a:1}

// Clone objC with properties named 'a' or 'inner'
extend.clone.keys(['a','inner'])(objC)  //-> {a:1, inner:{a:1}}

// Copy objA's & objB's properties that have a value greater than 2
extend.filter(value => value > 2)({}, objA, objB) //-> {b:3, c:4}

// Concat array values
extend({}, {arr:[1,2,3]}, {arr:[4,5,6]})        //-> {arr: [4,5,6]}
extend.concat({}, {arr:[1,2,3]}, {arr:[4,5,6]}) //-> {arr: [1,2,3,4,5,6]}

// And more...

Usage

extend(target, object1[, objectN...])

Shallow copy all properties (own & inherited) of object1 and any following objects into target.

extend[.<option>[.<option>...]](...)

Perform copy/extension with the specified options. options can be chained in any desired order and some accept arguments.

Example: extend.keys(['a', 'b']).clone.deep(targetObject) will deep clone targetObject's 'a' and 'b' properties.

Options

own

Only copies 'own' properties of object and not inherited properties.

Example:

var SomeConstructor = function(){this.a = 1; this.b = 2;}
SomeConstructor.prototype.inherited = 'abc'
var object = new SomeConstructor();

extend({}, object)      //-> {a:1, b:2, inherited:'abc'}
extend.own({}, object)  //-> {a:1, b:2}

deep

Performs a recursive copy of the specified objects.

Example:

var objA = {a:1, b:2, inner:{a:1, b:2}};
var objB = {b:3, c:4, inner:{b:3, c:4}};
var cloneA = extend({}, objA);
var cloneB = extend.deep({}, objA);

cloneA === objA //-> false
cloneA.inner === objA.inner //-> true
cloneB.inner === objA.inner //-> false

extend({}, objA, objB)      //-> {a:1, b:3, c:4, inner:{b:3, c:4}}
extend.deep({}, objA, objB) //-> {a:1, b:3, c:4, inner:{a:1, b:3, c:4}}

notDeep(array|object)

When paired with .deep it performs a recursive copy of the specified objects while performing a shallow copy for the provided keys.

Example:

var objA = {one:{a:1, b:2}, two:{a:1, b:2}};
var objB = {one:{b:3, c:4}, two:{b:3, c:4}};
var clone = extend.deep.notDeep(['two'])({}, objA, objB);

clone.one === objB.one //-> false
clone.two === objB.two //-> true
clone //-> {one:{a:1, b:3, c:4}, two:{b:3, c:4}};

deepOnly(array|object)

When paired with .deep it performs a recursive copy of the specified objects while performing a shallow copy for the provided keys.

Example:

var objA = {one:{a:1, b:2}, two:{a:1, b:2}};
var objB = {one:{b:3, c:4}, two:{b:3, c:4}};
var clone = extend.deep.deepOnly(['two'])({}, objA, objB);

clone.one === objB.one   //-> true
clone.two === objB.two   //-> false
clone                    //-> {one:{b:3, c:4}, two:{a:1, b:3, c:4}};

allowNull

Allows null values to be copied over.

Example:

var object = {a:1, b:null, c:3};

extend({}, object)            //-> {a:1, c:3}
extend.allowNull({}, object)  //-> {a:1, b:null, c:3}

nullDeletes

Encounters of null values in the source object will delete/remove the associated key in the target object.

Example:

var object = {nested:{a:1, b:2, c:3}, c:3};
var deletes = {nested{a:10}, c:null};

extend({}, object, deletes)                  //-> {nested:{a:10}, c:3}
extend.nullDeletes({}, object, deletes)      //-> {nested:{a:10}}
extend.nullDeletes.deep({}, object, deletes) //-> {nested:{a:10, b:2, c:3}}

clone

Clone the specified objects without specifying a target. This is basically a shortcut in which instead of passing an empty object as the first argument (i.e. the target object), an empty object will be created internally for you.

Example:

// Both will render the same results
var A = extend({}, {a:1}, {b:2})   //-> {a:1, b:2}
var B = extend.clone({a:1}, {b:2}) //-> {a:1, b:2}

concat

Causes array properties to be merged/concatenated instead of the usual behavior in which the 2nd array replaces the first. Behaves the same in both deep & shallow copies.

Example:

var objA = {arr: [1,2,3]};
var objB = {arr: [4,5,6]};
var objB2 = {arr: [null,4,5,6]};
extend({}, objA, objB)        //-> {arr: [4,5,6]}
extend.concat({}, objA, objB) //-> {arr: [1,2,3,4,5,6]}

extend({}, objA, objB2)       //-> {arr: [null,4,5,6]}
extend.deep({}, objA, objB2)  //-> {arr: [1,4,5,6]}
extend.deep.concat({}, objA, objB2)  //-> {arr: [1,2,3,4,5,6]}

keys(array|object)

Allows only properties whose name is included in the provided array. If a plain object is passed its keys/property names will be extracted into an array (disregarding the property values).

Example:

var objA = {a:1, b:2};
var objB = {b:3, c:4, d:5};

extend({}, objA, objB)                         //-> {a:1, b:3, c:4, d:5}
extend.keys(['a', 'c'])({}, objA, objB)        //-> {a:1, c:4}
extend.keys({b:true, d:false})({}, objA, objB) //-> {b:3, d:5}

notKeys(array|object)

Same as keys, but instead excludes and keys matching the one of the keys provided in the array/object.

Example:

var objA = {a:1, b:2};
var objB = {b:3, c:4, d:5};

extend({}, objA, objB)                            //-> {a:1, b:3, c:4, d:5}
extend.notKeys(['b', 'd'])({}, objA, objB)        //-> {a:1, c:4}
extend.notKeys({a:true, c:false})({}, objA, objB) //-> {b:3, d:5}

transform(transformFunction|transformMap)

Runs the provided transformFunction on each property encoutered in the provided sources with the following arguments: transformFunction(value, key, source). The value returned from the transformFunction will be used instead of the original value regardless if the transformed value is equal to undefined, null, or anything else. If provided a filter, transforms will be invoked only for properties that passed the filter predicate.

A transformMap object can be passed instead of a single function which is an object with the signature of {property: transformFunction}. When iterating through the source's properties, if there is a function predicate matching the currently processed property's name then it will be invoked and treated like a filterFunction.

Arguments:

  • transformFunction - a transform function to apply to each property encoutered in the source objects (i.e. the objects we are extending/copying).
    • value - The value of the current property being processed in the object.
    • key - The the name (or label) of the current property being processed in the object.
    • source - The object which this property belongs to.

Example (transformFunction):

var objA = {a:'a1', b:'b2'};
var objB = {b:'b3', c:'c4'};
var myTransform = function(value){return value.toUpperCase()}

extend.transform(myTransform)({}, objA, objB) //-> {a:'A1', b:'B3', c:'C4'}

Example (transformMap):

var objA = {a:'a1', b:'b2'};
var objB = {b:'b3', c:'c4'};

extend.transform({
    a: (value) => value.toUpperCase()
    c: (value) => value.toUpperCase()+'!'
})({}, objA, objB)
//-> {a:'A1', b:'b3', c:'C4!'}

filter(filterFunction|filterMap)

Runs the provided filterFunction on each property encoutered in the provided sources with the following arguments: filterFunction(value, key, source). The value returned from the filterFunction will be used to determine whether or not to copy the subject property - if the value is a truthy value the value property will be copied and if the value is a falsey value it will be omitted.

A filterMap object can be passed instead of a single function which is an object with the signature of {property: filterFunction}. When iterating through the source's properties, if there is a function predicate matching the currently processed property's name then it will be invoked and treated like a filterFunction.

Arguments:

  • filterFunction - a filter predicate to apply to each property encoutered. Return true to copy the property, otherwise return false to not copy the property.
    • value - The value of the current property being processed in the object.
    • key - The the name (or label) of the current property being processed in the object.
    • source - The object which this property belongs to.

Example (filterFunction):

var objA = {a:1, b:10};
var objB = {b:3, c:4, d:5};
var myFilter = function(value){return value > 3};

extend.filter(myFilter)({}, objA, objB) //-> {b:10, c:4, d:5}

Example (filterMap):

var objA = {a:1, b:5, c:3};
var objB = {a:3, b:2, c:5, e:0};
var objC = {a:10, b:'20', c:30, d:40};

extend.filter({
    a: (value) => value < 2
    b: (value) => typeof value === 'string'
    c: (value) => value < 10
    e: (value) => value > 0
})(objA, objB, objC)
//-> {a:1, b:'20', c:5, d:40}

License

MIT © Daniel Kalen