I'm looking for a function identical to lodash's _.set
except it should return a new object without modifying the original. Are there any implementations of this?
Right now I'm deep-cloning and then setting:
let data = _.cloneDeep(this.state.data);
_.set(data, name, value);
this.setState({data})
But I don't think it's necessary to do a full deep clone, we'd just need to do a shallow clone of the object at the deepest level, no?
e.g.
let oldData = { 'a': [{ 'b': { 'c': 3 }, 'd': {'e': 4} }] };
let newDate = withValue(oldData, 'a[0].b.c', 5);
The proposed withValue
function would perform a shallow clone of a
, a[0]
and a[0].b
and then modify the new a[0].b.c
to 5. We would not need to make a copy of a[0].d
.
I'm looking for a function identical to lodash's _.set
except it should return a new object without modifying the original. Are there any implementations of this?
Right now I'm deep-cloning and then setting:
let data = _.cloneDeep(this.state.data);
_.set(data, name, value);
this.setState({data})
But I don't think it's necessary to do a full deep clone, we'd just need to do a shallow clone of the object at the deepest level, no?
e.g.
let oldData = { 'a': [{ 'b': { 'c': 3 }, 'd': {'e': 4} }] };
let newDate = withValue(oldData, 'a[0].b.c', 5);
The proposed withValue
function would perform a shallow clone of a
, a[0]
and a[0].b
and then modify the new a[0].b.c
to 5. We would not need to make a copy of a[0].d
.
- No, that's not sufficient. This would not guarantee a mutation of the other object does not change the new one. So you either must deep-clone the plain JS object, or use something that wraps data structures for you (like immutablejs). – zerkms Commented May 1, 2016 at 21:46
- As of react - "unsetting" will not help, since react merges the passed object with the existing one. – zerkms Commented May 1, 2016 at 21:48
-
@zerkms What if I know the old one (
this.state.data
) will never be mutated so I don't have to worry about it altering the new one? – mpen Commented May 1, 2016 at 21:48 -
{...data, [name]: value}
<--- then that – zerkms Commented May 1, 2016 at 21:49 -
@zerkms React does a shallow merge when calling
setState
doesn't it? I've put all this data inthis.state.data
so unsetting something underthis.state.data
should unset it forreal, no? – mpen Commented May 1, 2016 at 21:50
2 Answers
Reset to default 6lodash/fp as Emil suggested should work.
Example:
const setIn = require('lodash/fp/set');
this.setState({data: setIn(name, value, this.state.data)});
N.B. the argument order is different than regular lodash.
This is going to be a multi-part answer.
Immutable set:
As you're using lodash, you can use the FP version of lodash and use .set
(or .setWith
to be able to supply an object path) which will do an immutable update.
Or, as this looks like a React codebase you can use the $set
helper of React.
Or you can just use the Spread Operator:
const newObj = {...oldObj, prop: newValue }
Or Object.assign:
const newObj = Object.assign({}, oldObj, { prop: newValue });
Performance:
As you're mentioning performance (and not just deeply cloning an object) you might want to look in to an immutable framework. The more developed codebases increase performance by different measures. E.g. here's what immutable.js says:
These data structures are highly efficient on modern JavaScript VMs by using structural sharing via hash maps tries and vector tries
...and Mori says:
Efficient immutable data structures - no cloning required [...] Modern JavaScript engines like V8, JavaScriptCore, and SpiderMonkey deliver the performance needed to implement persistent data structures well.