Gathering detailed insights and metrics for @clevercanyon/js-object-mc
Gathering detailed insights and metrics for @clevercanyon/js-object-mc
npm install @clevercanyon/js-object-mc
Typescript
Module System
Node Version
NPM Version
75
Supply Chain
100
Quality
79.2
Maintenance
100
Vulnerability
100
License
JavaScript (100%)
Love this project? Help keep it running — sponsor us today! 🚀
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
146 Commits
3 Branches
1 Contributors
Updated on Oct 19, 2023
Latest Version
1.8.4
Package Id
@clevercanyon/js-object-mc@1.8.4
Unpacked Size
76.94 kB
Size
17.65 kB
File Count
25
NPM Version
8.1.4
Node Version
17.2.0
Published on
Dec 14, 2021
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
Install this fork with NPM as follows:
1$ npm install --save @clevercanyon/js-object-mc;
As noted, this is a fork of VladimirShestakov/merge-change that has been patched to resolve this prototype pollution security issue. The original, but slightly modified README file continues below.
Simple library for deep merge of objects and other types, also for patches and immutable updates.
By default, merge works for "plain objects".
Values of other types are replaced, but you can customize merging between specific types.
Also, you can use declarative operations to specific merge like unset
, leave
, push
and other.
For example to remove some properties of object, to replace "plain objects", to concat arrays.
Calculating diffs between two values.
Install with NPM:
1$ npm install --save @clevercanyon/js-object-mc;
Merge with deep cloning without changing the source objects. Great for creating or extending objects from the example (source).
1mc.merge(source, ...values);
Example
1const mc = require('@clevercanyon/js-object-mc'); 2 3// Create new object with adding "a.three" and deleting "a.one" 4let first = { 5 a: { 6 one: true, 7 two: 2 8 } 9}; 10let second = { 11 a: { 12 three: 3, 13 $unset: ['one'] // $unset is a declarative operations 14 } 15}; 16 17const result = mc.merge(first, second); 18 19console.log(result);
1{ a: { two: 2, three: 3} }
Merge with mutation of the source objects. Nice for patching. New instances will not be created.
1mc.patch(source, ...patches);
1let first = { 2 a: { 3 one: true, 4 two: 2 5 } 6}; 7let second = { 8 a: { 9 three: 3, 10 $unset: ['one'] // $unset is a declarative operations 11 } 12}; 13 14const result = mc.patch(first, second); // => { a: { two: 2, three: 3} } 15 16// result is a mutated first argument 17console.log(result === first); // => true 18console.log(result !== second); // => true
Immutable merge - create new instances only if there are diffs (also in inner properties). Nice for state management.
1mc.update(source, ...changes);
1let first = { 2 a: { 3 one: true, 4 two: 2, 5 sub: { 6 value: 3 7 } 8 } 9}; 10let second = { 11 a: { 12 three: 3, 13 $unset: ['one'] // $unset is a declarative operations 14 } 15}; 16 17const result = mc.update(first, second); // => { a: { two: 2, three: 3, sub: { value: 3 }} } 18 19// result is a new object 20console.log(result !== first); // => true 21console.log(result !== second); // => true 22 23// object "a.sub" is unchanged 24console.log(result.a.sub === first.a.sub); // => true
When merging objects, you can perform delete and replace properties at the same time. Use declarative operations in second or next arguments. Supported in all merge methods. The syntax is similar to mongodb.
$set
To set (or replace) property without deep merge.
1const result = mc.merge( 2 { 3 a: { 4 one: 1, 5 two: 2 6 } 7 }, 8 { 9 $set: { 10 a: { 11 three: 3 12 }, 13 'a.two': 20 // Fields keys can be path. 14 } 15 } 16); 17console.log(result);
Result
1{ 2 "a": { 3 "one": 1, 4 "two": 20, 5 "three": 3 6 } 7}
$unset
To unset properties by name (or path)
1const result = mc.merge( 2 { 3 a: { 4 one: 1, 5 two: 2 6 } 7 }, 8 { 9 $unset: ['a.two'] 10 } 11); 12console.log(result);
Result
1{ 2 "a": { 3 "one": 1 4 } 5}
*
1const result = mc.merge( 2 { 3 a: { 4 one: 1, 5 two: 2 6 } 7 }, 8 { 9 $unset: ['a.*'] 10 } 11); 12console.log(result);
Result
1{ 2 "a": {} 3}
$leave
To leave properties by name (or path). All other properties will be removed.
1const result = mc( 2 { 3 a: { 4 one: 1, 5 two: 2, 6 tree: 3 7 } 8 }, 9 { 10 a: { 11 $leave: ['two'] 12 } 13 } 14); 15console.log(result);
Result
1 { 2 "a": { 3 "two": 2 4 } 5 }
$push
To push one value to the array property. The source property must be an array.
1const result = mc( 2 // First object 3 { 4 prop1: ['a', 'b'], 5 prop2: ['a', 'b'], 6 }, 7 // Merge 8 { 9 $push: { 10 prop1: ['c', 'd'], 11 prop2: {x: 'c'} 12 }, 13 } 14); 15console.log(result);
Result
1{ 2 "prop1": ["a", "b", ["c", "d"]], 3 "prop2": ["a", "b", {"x": "c"}] 4}
$concat
To concatenate arrays. The source property must be an array. The property in secondary arguments may not be an array.
1const result = mc( 2 // First object 3 { 4 prop1: ['a', 'b'], 5 prop2: ['a', 'b'], 6 }, 7 // Merge 8 { 9 $concat: { 10 prop1: ['c', 'd'], 11 prop2: {x: 'c'} 12 }, 13 } 14); 15console.log(result);
Result
1{ 2 "prop1": ["a", "b", "c", "d"], 3 "prop2": ["a", "b", {"x": "c"}] 4}
You can declare function for merge custom types (or override default logic). Returns previous merge method.
mc.addMerge(type1, type2, callback)
type1, type2
- constructor name of the first and second values: Number, String, Boolean, Object, Array, Date, RegExp, Function, Undefined, Null, Symbol, Set, Map
and other system and custom constructor namescallback
- merge function with argument: (first, second, kind)
first
- first value for mergesecond
- second value for mergekind
- name of merging method, such as "merge", "patch", "update".For example, if you always need to union arrays, you can declare method to merge array with array.
1const previous = mc.addMerge('Array', 'Array', function(first, second, kind){ 2 // merge - creaete new array with deep clone 3 if (kind === 'merge'){ 4 return first.concat(second).map(item => mc.merge(undefined, item)); 5 } 6 // patch - mutate first array 7 if (kind === 'patch'){ 8 first.splice(first.length, 0, ...second); 9 return first; 10 } 11 // update - return first array if second is empty, or create new without clone 12 if (second.length === 0){ 13 return first; 14 } else { 15 return first.concat(second); 16 } 17}); 18 19// reset custom method 20mc.addMerge('Array', 'Array', previous);
You can declare function for declarative operation (or override default logic). Returns previous operation method.
mc.addOperation(name, callback)
name
- operation name, for example "$concat"callback
- operation function with argument: (source, params). Return new value or source.
source
- the value in which the operation is defined (source: {$concat: params}
)params
- value of operator ($concat: params
)For example, if sometimes need to union arrays, you can declare declarative operation $concat (it exists in the library).
1const previous = mc.addOperation('$concat', function(source, params){
2 const paths = Object.keys(params);
3 for (const path of paths) {
4 let value = params[path];
5 let array = utils.get(source, path, []);
6 if (Array.isArray(array)) {
7 array = array.concat(value);
8 utils.set(source, path, array);
9 } else {
10 throw new Error('Cannot concat on not array');
11 }
12 }
13 return paths.length > 0;
14});
15
16// reset custom operation
17mc.addOperation('$concat', previous);
Useful functions - utilities
1const utils = require('@clevercanyon/js-object-mc').utils;
utils.diff(source, compare, {ignore = [], separator = '.'})
To calculate the difference between source
and compare
value.
The return value is an object with $set
and $unset
operators. Return value can be used in merge functions.
The ignore
parameter - is a list of properties that are not included in the comparison.
1const first = { 2 name: 'value', 3 profile: { 4 surname: 'Surname', 5 birthday: new Date(), 6 avatar: { 7 url: 'pic.png' 8 } 9 }, 10 access: [100, 350, 200], 11 secret: 'x' 12} 13 14const second = { 15 login: 'value', 16 profile: { 17 surname: 'Surname2', 18 avatar: { 19 url: 'new/pic.png' 20 } 21 }, 22 access: [700] 23} 24 25const diff = utils.diff(first, second, {ignore: ['secret'], separator: '/'});
Result (diff)
{
$set: {
'login': 'value',
'profile.surname': 'Surname2',
'profile.avatar.url': 'new/pic.png',
'access': [ 700 ]
},
$unset: [
'profile.birthday',
'name'
]
}
utils.type(value)
Get real type of any value. The return value is a string - the name of the constructor.
1utils.type(null); // => 'Null' 2utils.type(true); // => 'Boolean' 3utils.type(new ObjectId()); // => 'ObjectID'
utils.instanceof(value, className)
Checking instance of class. className
is string (not constructor). The return value is a boolean.
1utils.instanceof(100, 'Number'); // => true 2utils.instanceof(new MyClass(), 'MyClass'); // => true 3utils.instanceof(new MyClass(), 'Object'); // => true
utils.plain(value)
Converting deep value to plain types if value has plain representation. For example, all dates are converted to a string, but RegEx not.
To customize conversion, you can define the [methods.toPlain]()
method in your object.
Nice for unit tests.
The method is similar to converting to JSON, only objects (arrays, functions...) are not converted to string representation.
1const plain = utils.plain({ 2 date: new Date('2021-01-07T19:10:21.759Z'), 3 prop: { 4 _id: new ObjectId('6010a8c75b9b393070e42e68') 5 } 6});
Result (plain)
{
date: '2021-01-07T19:10:21.759Z',
prop: {
_id: '6010a8c75b9b393070e42e68'
}
}
utils.flat(value, path = '', separator = '.', clearUndefined = false)
Converting a nested structure to a flat object.
Property names become path with separator
.
To customize conversion, you can define the [methods.toFlat]()
method in your object.
1const value = { 2 a: { 3 b: { 4 c: 100 5 } 6 } 7}; 8const flat = utils.flat(value, 'parent', '.');
Result (flat)
{
'parent.a.b.c': 100
}
No vulnerabilities found.
No security vulnerabilities found.