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.
- 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.
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...
Shallow copy all properties (own & inherited) of object1
and any following objects into target
.
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.
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}
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}}
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}};
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}};
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}
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 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}
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]}
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}
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}
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!'}
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. Returntrue
to copy the property, otherwise returnfalse
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}
MIT © Daniel Kalen