Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
immutable-assign
Advanced tools
Lightweight immutable helper that allows you to continue working with Plain JavaScript Objects
Lightweight immutable helper that allows you to continue working with POJO (Plain Old JavaScript Object), and supports full TypeScript type checking for nested objects.
This library is trying to solve following problems:
This library is an alternative to Immutable.js, it has only one method iassign(), which accept a POJO object and return you a new POJO object with specific property updated. However, since it works with other libraries such as lodash (refer to example 4), it provides all the functionalities you need plus immutability.
This library will leave your POJO objects completely untouched (except the optional deep-freeze), it does not wrap around nor add any methods/properties to your POJO objects.
This library works in JavaScript and it works really well with TypeScript, because of its generic type argument inference; and since you are working with POJO (not the wrapper objects), you can utilize the full power of TypeScript: IntelliSense, type checking and refactoring, etc.
Performance of this library should be comparable to Immutable.js, because read operations will always occur more than write operations. When using this library, all your react components can read object properties directly. E.g., you can use <TextBox value={this.state.userinfo.fullName} /> in your components, instead of <TextBox value={this.state.getIn(["userinfo", "fullName"])} />. In addition, shouldComponentUpdate() can compare POJO objects without knowing about the immutable libraries, e.g., return this.props.userInfo.orders !== nextProps.userInfos.orders. I.e., the more read operations you have, the more it will outperform Immutable.js. Following are the benchmarks for multiple immutable libraries (assuming the read to write ratio is 5 to 1):
npm run benchmarks
npm install immutable-assign --save
var iassign = require("immutable-assign");
// Deep freeze both input and output, can be used in development to make sure they don't change.
iassign.freeze = true;
var map1 = { a:1, b:2, c:3 };
// 1: Calling iassign() to update map1.b, using overload 2
var map2 = iassign(
map1,
function (m) { m.b = 50; return m; }
);
// map2 = { a:1, b: 50, c:3 }
// map2 !== map1
var iassign = require("immutable-assign");
var list1 = [1, 2];
// 2.1: Calling iassign() to push items to list1, using overload 2
var list2 = iassign(
list1,
function (l) { l.push(3, 4, 5); return l; }
);
// list2 = [1, 2, 3, 4, 5]
// list2 !== list1
// 2.2: Calling iassign() to unshift item to list2, using overload 2
var list3 = iassign(
list2,
function (l) { l.unshift(0); return l; }
);
// list3 = [0, 1, 2, 3, 4, 5]
// list3 !== list2
// 2.3, Calling iassign() to concat list1, list2 and list3, using overload 2
var list4 = iassign(
list1,
function (l) { return l.concat(list2, list3); }
);
// list4 = [1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]
// list4 !== list1
// 2.4, Calling iassign() to concat sort list4, using overload 2
var list5 = iassign(
list4,
function (l) { return l.sort(); }
);
// list5 = [0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5]
// list5 !== list4
var iassign = require("immutable-assign");
var nested1 = { a:{ b:{ c:[3, 4, 5] } } };
// 3.1: Calling iassign() to assign d to nested1.a.b
var nested2 = iassign(
nested1,
function (n) { return n.a.b; },
function (b) { b.d = 6; return b; }
);
// nested2 = { a:{ b:{ c:[3, 4, 5], d: 6 } } }
// nested2 !== nested1
// 3.2: Calling iassign() to increment nested2.a.b.d
var nested3 = iassign(
nested2,
function (n) { return n.a.b.d; },
function (d) { return d + 1; }
);
// nested3 = { a:{ b:{ c:[3, 4, 5], d: 7 } } }
// nested3 !== nested2
// 3.3: Calling iassign() to push item to nested3.a.b.c
var nested4 = iassign(
nested3,
function (n) { return n.a.b.c; },
function (c) { c.push(6); return c; }
);
// nested4 = { a:{ b:{ c:[3, 4, 5, 6], d: 7 } } }
// nested4 !== nested3
var iassign = require("immutable-assign");
var _ = require("lodash");
var nested1 = { a: { b: { c: [1, 2, 3] } } };
// 4.1: Calling iassign() and _.map() to increment to every item in "c" array
var nested2 = iassign(
nested1,
function (n) { return n.a.b.c; },
function (c) {
return _.map(c, function (i) { return i + 1; });
}
);
// nested2 = { a: { b: { c: [2, 3, 4] } } };
// nested2 !== nested1
// 4.2: Calling iassign() and _.flatMap()
var nested3 = iassign(
nested2,
function (n) { return n.a.b.c; },
function (c) {
return _.flatMap(c, function (i) { return [i, i]; });
}
);
// nested3 = { a: { b: { c: [2, 2, 3, 3, 4, 4] } } };
// nested3 !== nested2
var iassign = require("immutable-assign");
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} };
// 5: Calling iassign() to increment o1.a.b.c[0][0].d
var o2 = iassign(
o1,
function (o) { return o.a.b.c[0][0]; },
function (ci) { ci.d++; return ci; }
);
//
// Jasmine Tests
//
// expect o1 has not been changed
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} });
// expect o2 inner property has been updated.
expect(o2.a.b.c[0][0].d).toBe(12);
// expect object graph for changed property in o2 is now different from (!==) o1.
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]);
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]);
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d);
// expect object graph for unchanged property in o2 is still equal to (===) o1.
expect(o2.a2).toBe(o1.a2);
expect(o2.a.b2).toBe(o1.a.b2);
expect(o2.a.b.c2).toBe(o1.a.b.c2);
expect(o2.a.b.c[0][0].e).toBe(o1.a.b.c[0][0].e);
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]);
var iassign = require("immutable-assign");
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} };
// 6: Calling iassign() to push new item to o1.a.b.c[1]
var o2 = iassign(
o1,
function (o) { return o.a.b.c[1]; },
function (c) { c.push(101); return c; }
);
//
// Jasmine Tests
//
// expect o1 has not been changed
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} });
// expect o2 inner property has been updated.
expect(o2.a.b.c[1][1]).toBe(101);
// expect object graph for changed property in o2 is now different from (!==) o1.
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]);
// expect object graph for unchanged property in o2 is still equal to (===) o1.
expect(o2.a2).toBe(o1.a2);
expect(o2.a.b2).toBe(o1.a.b2);
expect(o2.a.b.c2).toBe(o1.a.b.c2);
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]);
expect(o2.a.b.c[0][0]).toBe(o1.a.b.c[0][0]);
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]);
var iassign = require("immutable-assign");
var o1 = { a: { b: { c: [{ d: 11, e: 12 }, { d: 21, e: 22 }] } } };
// 7: Calling iassign() to push increment to o1.a.b.c[0].d
var external = { a: 0 };
var o2 = iassign(
o1,
function (o, ctx) { return o.a.b.c[ctx.external.a]; },
function (ci) { ci.d++; return ci; },
{ external: external }
);
//
// Jasmine Tests
//
// expect o1 has not been changed
expect(o1).toEqual({ a: { b: { c: [{ d: 11, e: 12 }, { d: 21, e: 22 }] });
// expect o2 inner property has been updated.
expect(o2.a.b.c[external.a].d).toBe(12);
// expect object graph for changed property in o2 is now different from (!==) o1.
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]);
expect(o2.a.b.c[0].d).not.toBe(o1.a.b.c[0].d);
// expect object graph for unchanged property in o2 is still equal to (===) o1.
expect(o2.a.b.c[0].e).toBe(o1.a.b.c[0].e);
expect(o2.a.b.c[1]).toBe(o1.a.b.c[1]);
expect(o2.a.b.c[1].d).toBe(o1.a.b.c[1].d);
expect(o2.a.b.c[1].e).toBe(o1.a.b.c[1].e);
var iassign = require("immutable-assign");
var nested1 = { a: { b: { c: [3, 4, 5] } } };
// 8.1: Calling iassign() to assign d to nested1.a.b
var iassignFp = iassign.fp(undefined)
(function (n) { return n.a.b; })
(function (b) { b.d = 6; return b; })
(undefined);
var nested2 = iassignFp(nested1);
// nested2 = { a: { b: { c: [3, 4, 5], d: 6 } } };
// nested2 !== nested1
// 8.2: Calling iassign() to increment nested2.a.b.d
iassignFp = iassign.fp(undefined)
(function (n) { return n.a.b.d; })
(function (d) { return d + 1; })
(undefined);
var nested3 = iassignFp(nested2);
// nested3 = { a: { b: { c: [3, 4, 5], d: 7 } } };
// nested3 !== nested2
// 8.3: Calling iassign() to push item to nested3.a.b.c
iassignFp = iassign.fp(undefined)
(function (n) { return n.a.b.c; })
(function (c) { c.push(6); return c; })
(undefined);
var nested4 = iassignFp(nested3);
// nested4 = { a: { b: { c: [3, 4, 5, 6], d: 7 } } };
// nested4 !== nested3
// 8.4: Calling iassign() to push item to nested3.a.b.c[1]
iassignFp = iassign.fp(undefined)
(function (n, ctx) { return n.a.b.c[ctx.i]; })
(function (ci) { return ci + 100; })
({i: 1});
var nested5 = iassignFp(nested4);
// nested5 = { a: { b: { c: [3, 104, 5, 6], d: 7 } } };
// nested5 !== nested4
var iassign = require("immutable-assign");
var map1 = new Map();
map1.set("a", "value a");
iassign.setOption({
copyFunc: function (value, propName) {
if (value instanceof Map) {
// In IE11, Map constructor arguments are not supported,
// you need to provide ES6 shim, e.g., use core-js
return new Map(value);
}
}
});
var map2 = iassign(
map1,
m => { m.set(1, 'first'); return m; }
);
// map2 !== map1
// map1 = Map({ "a": "value a" })
// map1 = Map({ "a": "value a", 1: "first" })
// Return a new POJO object with property updated.
// function overload 1:
iassign = function<TObj, TProp, TContext>(
obj: TObj, // POJO object to be getting the property from, it will not be modified.
getProp: (obj: TObj, context: TContext) => TProp, // Function to get the property that needs to be updated.
setProp: (prop: TProp) => TProp, // Function to set the property.
context?: TContext, // (Optional) Context to be used in getProp().
option?: IIassignOption): TObj; // (Optional) Options
// function overload 2: you can skip getProp() if you trying to update the root object, refer to example 1 and 2
iassign = function<TObj>(
obj: TObj, // POJO object to be getting the property from, it will not be modified.
setProp: setPropFunc<TObj>, // Function to set the property.
option?: IIassignOption): TObj; // (Optional) Options
// functional programming friendly style, moved obj to the last parameter and supports currying, refer to example 8
iassign.fp = function <TObj, TProp, TContext>(
option: IIassignOption,
getProp: getPropFunc<TObj, TProp, TContext>,
setProp: setPropFunc<TProp>,
context?: TContext,
obj?: TObj): TObj; // POJO object to be getting the property from, it will not be modified.
// In ES6, you cannot set property on imported module directly, because they are default
// to readonly, in this case you need to use this method.
iassign.setOption(option: IIassignOption): void;
// Options, can be applied globally or individually
interface IIassignOption {
freeze?: boolean; // Deep freeze both input and output
freezeInput?: boolean; // Deep freeze input
freezeOutput?: boolean; // Deep freeze output
useConstructor?: boolean; // Uses the constructor to create new instances
// Custom copy function, can be used to handle special types, e.g., Map, Set; refer to example 9
copyFunc?: <T>(value: T, propName: string): T;
// Disable validation for extra statements in the getProp() function,
// which is needed when running the coverage, e.g., istanbul.js does add
// instrument statements in our getProp() function, which can be safely ignored.
disableExtraStatementCheck?: boolean;
// Return the same object if setProp() returns its parameter (i.e., reference pointer not changed).
ignoreIfNoChange?: boolean;
}
2.0.4 - Replaced the proxy-polyfill with Object.defineProperty(), which has much better browser support.
2.0.1 - Minor bug fixes.
2.0.0 -
1.0.36 - Supports ES6 Map and Set. Refer to example 9
1.0.35 - Supports ES6 default export.
1.0.31 -
1.0.30 - Support classes
1.0.29 - Supported ES6 Arrow Functions
1.0.27 - Added iassign.fp() that support currying, refer to example 8
1.0.26 - Works with webpack, please refer to ImmutableAssignTest
1.0.23 - Greatly improved performance.
1.0.21 - Added new function overload to skip getProp() if you trying to update the root object, refer to example 1 and example 2
1.0.20 - Added Travis-CI, Coveralls (coverage) and SauceLabs (browsers' tests)
1.0.19 - Added TypeScript types to package.json
1.0.18 - Tested on Mac (Safari 10 and Chrome 54)
1.0.16 - Tested in Node.js and major browsers (IE 11, Chrome 52, Firefox 47, Edge 13, PhantomJS 2)
FAQs
Lightweight immutable helper that allows you to continue working with Plain JavaScript Objects
The npm package immutable-assign receives a total of 3,801 weekly downloads. As such, immutable-assign popularity was classified as popular.
We found that immutable-assign demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.