putil-merge
Advanced tools
Comparing version 1.0.2 to 1.1.0
184
lib/merge.js
/* putil-merge | ||
------------------------ | ||
(c) 2017-present Panates | ||
SQB may be freely distributed under the MIT license. | ||
This file may be freely distributed under the MIT license. | ||
For details and documentation: | ||
https://panates.github.io/putil-merge/ | ||
https://github.com/panates/putil-merge | ||
*/ | ||
function merge(target, ...sources) { | ||
return _merge({}, target, ...sources); | ||
} | ||
function merge(options, target, sources) { | ||
if (typeof target !== 'object') | ||
return target; | ||
merge.init = function(cfg) { | ||
return function(target, ...sources) { | ||
return _merge(cfg, target, ...sources); | ||
if (!sources) { | ||
if (!target) { | ||
target = options; | ||
sources = null; | ||
} else { | ||
sources = target; | ||
target = options; | ||
} | ||
options = {}; | ||
} | ||
if (sources && !Array.isArray(sources)) | ||
sources = [sources]; | ||
var visited = Map(); | ||
const mergeValue = function(target, source, clone, deep) { | ||
if (typeof source === 'object') { | ||
// Circular reference detection | ||
var o; | ||
if ((o = visited.get(source))) | ||
return o; | ||
// If array | ||
if (Array.isArray(source)) { | ||
if (clone) | ||
target = cloneArray(source, clone); | ||
else target = source; | ||
} | ||
// If object | ||
else if (isObject(source)) { | ||
if (clone || deep) { | ||
target = mergeObject((isObject(target) && deep ? target : {}), | ||
source, clone, deep); | ||
} else target = source; | ||
} | ||
// Add to map for circular reference detection | ||
visited.put(source, target); | ||
return target; | ||
} | ||
return source; | ||
}; | ||
}; | ||
merge.deep = function(target, ...sources) { | ||
return _merge({deep: true}, target, ...sources); | ||
}; | ||
const mergeObject = function(target, source, clone, deep) { | ||
if (source === target) return target; | ||
Object.getOwnPropertyNames(source).forEach(function(key) { | ||
var src = Object.getOwnPropertyDescriptor(source, key); | ||
var trg = Object.getOwnPropertyDescriptor(target, key); | ||
if (options.filter && !options.filter(source, key, src)) | ||
return; | ||
var val = mergeValue(trg && trg.value, src.value, clone, deep); | ||
if (options.descriptor) { | ||
src.val = val; | ||
Object.defineProperty(target, key, src); | ||
} else target[key] = val; | ||
}); | ||
return target; | ||
}; | ||
merge.deepClone = function(target, ...sources) { | ||
return _merge({deep: true, clone: true}, target, ...sources); | ||
}; | ||
const cloneArray = function(source, cloneObjects) { | ||
if (cloneObjects) { | ||
// Clone object items | ||
target = new Array(source.length); | ||
source.forEach(function(v, i) { | ||
target[i] = mergeValue(null, v, true); | ||
}); | ||
return target; | ||
} | ||
return source.slice(); | ||
}; | ||
merge.clone = function(target, ...sources) { | ||
return _merge({clone: true}, target, ...sources); | ||
}; | ||
if (options.clone) | ||
target = mergeObject({}, target, true, true); | ||
function _merge(cfg, target, ...sources) { | ||
if (sources) | ||
sources.forEach(function(source, idx) { | ||
if (!isObject(source)) | ||
throw new TypeError('Invalid source object at (' + idx + | ||
'). Can`t merge different types'); | ||
target = mergeObject(target, source, options.clone, options.deep); | ||
}); | ||
if (!isObject(target)) return target; | ||
return target; | ||
} | ||
if (cfg.clone) | ||
target = JSON.parse(JSON.stringify(target)); | ||
makeSequence(merge); | ||
sources.forEach(source => { | ||
if (isObject(source)) { | ||
const keys = Object.getOwnPropertyNames(source); | ||
for (const key of keys) { | ||
const src = source[key]; | ||
if (!cfg.filter || cfg.filter(key, src)) { | ||
if (isObject(src)) { | ||
if (!isObject(target[key])) | ||
target[key] = {}; | ||
if (cfg.deep) | ||
target[key] = _merge(cfg, target[key], src); | ||
else if (cfg.clone) | ||
target[key] = JSON.parse(JSON.stringify(src)); | ||
else target[key] = src; | ||
} else if (Array.isArray() && cfg.clone) | ||
target[key] = JSON.parse(JSON.stringify(src)); | ||
else | ||
target[key] = src; | ||
} | ||
function makeSequence(m) { | ||
const bindFn = function(fn) { | ||
if (!fn.options) { | ||
var options = {}; | ||
fn = function(target, sources) { | ||
return merge(options, target, sources); | ||
}; | ||
fn.options = options; | ||
makeSequence(fn); | ||
} | ||
return fn; | ||
}; | ||
Object.defineProperties(m, { | ||
clone: { | ||
get: function() { | ||
const fn = bindFn(this); | ||
fn.options.clone = true; | ||
return fn; | ||
} | ||
}, | ||
deep: { | ||
get: function() { | ||
const fn = bindFn(this); | ||
fn.options.deep = true; | ||
return fn; | ||
} | ||
}, | ||
descriptor: { | ||
get: function() { | ||
const fn = bindFn(this); | ||
fn.options.descriptor = true; | ||
return fn; | ||
} | ||
}, | ||
filter: { | ||
value: function(filterFn) { | ||
const fn = bindFn(this); | ||
fn.options.filter = filterFn; | ||
return fn; | ||
} | ||
} | ||
}); | ||
return target; | ||
} | ||
@@ -67,2 +148,21 @@ | ||
function Map() { | ||
var keys = [], values = []; | ||
return { | ||
put: function(key, value) { | ||
var index = keys.indexOf(key); | ||
if (index < 0) { | ||
keys.push(key); | ||
values.push(value); | ||
} else { | ||
values[index] = value; | ||
} | ||
}, | ||
get: function(key) { | ||
return values[keys.indexOf(key)]; | ||
} | ||
}; | ||
} | ||
module.exports = merge; |
{ | ||
"name": "putil-merge", | ||
"description": "Lightweight solution for merging multiple objects into one. Also it supports deep merge and deep clone", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"author": "Panates Ltd.", | ||
@@ -19,7 +19,7 @@ "contributors": [ | ||
"devDependencies": { | ||
"babel-eslint": "^7.2.3", | ||
"eslint": "^3.19.0", | ||
"babel-eslint": "^8.0.0", | ||
"eslint": "^4.7.2", | ||
"eslint-config-google": "^0.9.1", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^3.4.2" | ||
"mocha": "^3.5.3" | ||
}, | ||
@@ -26,0 +26,0 @@ "engines": { |
@@ -10,38 +10,91 @@ # putil-merge | ||
Lightweight solution for merging multiple objects into one. Also it supports deep merge | ||
A 'swiss army knife' solution for merging multiple objects into one. It supports deep merge, cloning objects, copying descriptors and filtering. | ||
## Installation | ||
- `npm install putil-merge --save` | ||
`$ npm install putil-merge --save` | ||
## Usage | ||
`merge(target, ...source)` | ||
`merge([config], target, [source])` | ||
Combines source objects with the target object without deep operation. Also it copies references instead of cloning. | ||
- config [`Object`] | ||
- deep [`Boolean`]: If true, it performs deep merge operation. | ||
- clone [`Boolean`]: If true, it clones objects except setting references | ||
- descriptor [`Boolean`]: If true, it copies descriptors | ||
- filter [`Function(obj, key, value)`] | ||
- target [`Object`] | ||
- source [`Object|Array<Object>`] | ||
## Sequential calling | ||
`merge.deep(target, ...source)` | ||
It supports sequential calling style. | ||
Combines source objects with the target object with deep operation. Also it copies references instead of cloning. | ||
`merge.(option).(option)...(target, source)` | ||
`merge.clone(target, ...source)` | ||
Etc: | ||
Combines source objects with the target object without deep operation. Also it clones values. | ||
`merge(target, source)` | ||
`merge.deepClone(target, ...source)` | ||
Combines source objects with the target object with deep operation. Also it clones values. | ||
Merge source object over target object with deep operation: | ||
`merge.deep(target, source)` | ||
```javascript | ||
const a = {l1a: 1, l1b: '2', l1d: {l2a: 1}}; | ||
const b = {l1b: 'b', l1c: 3, l1d: {l2b: {l3a: '2'}}, l1e: [1, 2, 3, 4]}; | ||
let o = merge(a, b); | ||
Merge source object over target object with deep operation and cloning. | ||
`merge.deep.clone(target, source)` | ||
`merge.clone.deep(target, source)` | ||
Merge source object descriptors over target object: | ||
`merge.descriptor(target, source)` | ||
Merge source object descriptors over target object with deep operation: | ||
`merge.deep.descriptor(target, [source1, source2])` | ||
`merge.clone.descriptor(target, source)` | ||
`merge.deep.clone.descriptor(target, [source1, source2])` | ||
`merge.deep.clone.descriptor(target, source)` | ||
`merge.clone.deep.descriptor(target, source)` | ||
`merge.descriptor.clone.deep(target, source)` | ||
`merge.descriptor.filter(filterfn)(target, source)` | ||
## Examples | ||
Merge source object over target object: | ||
```js | ||
const a = {prm1: 1, prm2: 2}; | ||
const b = {prm1: 11, prm3: [1, 2, 3, 4]}; | ||
var merged = merge(a, b); | ||
``` | ||
Clone any object: | ||
```js | ||
const a = {prm1: 1, prm2: { prm3: 3}}; | ||
var cloned = merge.clone(a); | ||
var deepCloned = merge.deep.clone(a); | ||
``` | ||
Merge source object over target object with custom filtering: | ||
```js | ||
var a = {id: 1}; | ||
var b = {name: 'John', surname: 'Wick'}; | ||
var merged = merge.deep.filter(function(o,k,v){ | ||
return k === 'name'; | ||
})(target, source); | ||
``` | ||
## Node Compatibility | ||
- node `>= 6.x`; | ||
- node `>= 0.10`; | ||
@@ -48,0 +101,0 @@ ### License |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9658
150
119