var b = {};
var a = b;
b.test = 123;
console.log(a.test);
I am trying to write code similar to the above, however for sake of not having to describe context I'll display that instead ^
After the line a = b I want to lose the reference from a to b, so I can update b without it affecting a, and vice-versa
Is this possible?
You can clone your object with Object.assign():
var a = Object.assign({}, b);
You can use JSON.stringify(obj) and then JSON.parse to the string.
So it'll be something like that:
let obj= {
hello: 'hello'
};
let string = JSON.stringify(obj);
let newObj = JSON.parse(string);
Or a shorter, one-line way:
let newObj = JSON.parse(JSON.stringify(obj))
Using Object.assign(), Object.create() or spread operator will not help you with arrays in objects (works with non-array properties though).
let template = {
array: []
};
let copy = { ...template };
console.log(template.array); // As expected, results in: []
copy.array.push(123);
console.log(template.array); // Output: [123]
But this can be solved using JSON.stringify() and JSON.parse(), as already said.
let template = {
array: []
};
let copy = JSON.parse(JSON.stringify(template));
console.log(template.array); // Output: []
copy.array.push(123);
console.log(template.array); // Output: []
Wonder if it is the most adequate solution...
Excuses if I'm missing something, am only a beginner.
Related
I have an array of object. For doing one opetation I need to remove few fields from this object and for some other operation I have to use the whole fields
But both of the array removing "regex" field. What is the mistake I am doing here?
var newob = {};
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
newob = JSON.stringify(myObject);
delete newob.regex;
console.log("Test1", newob);
console.log("Test2", myObject);
You're missing JSON.parse so you can create the object back. Otherwise, you're trying to delete a property regex of a string, which doesn't exist.
newob = JSON.parse(JSON.stringify(myObject));
JSON.stringify creates a string, you need to do JSON.parse to create an object from a JSON string.
For that object, you can use Object.assign({}, myObject) since it's a shallow clone.
newob = Object.assign({}, myObject);
// newobj = { ...myObject } // this will also work
The problem is that newObj is not a copy of myObject but a reference to it. Thus when you delete the filed of myObject you also see the change in newObj
To explain what I have said, look at this snippet:
> const a = {a: 1}
undefined
> b = a
{ a: 1 }
> a.c = 58
58
> b
{ a: 1, c: 58 }
You can copy the object like this:
const newobj = JSON.parse(JSON.stringify(myObject));
then changes on myObject won't affect newobj
There is an other problem:
newob = JSON.stringify(myObject);
is wrong here because you assign you want an object but JSON.stringify returns a string
If I understand you correctly you want to map the input data and remove the regex from each of the objects in your input array.
const items = [
{
ircEvent: 'PRIVMSG',
method: 'newURI',
regex: '^http://.*',
},
{
ircEvent: 'TEST',
method: 'newURI',
regex: '^http://.*',
},
]
const newItems = items.map(({ regex, ...item }) => item)
A good way to explain what is happening above is
const newArray = array.map(({dropAttr1, ...keepAttrs}) => keepAttrs)
But should you want to remove the key from one object only you could
const myObject = {
ircEvent: 'PRIVMSG',
method: 'newURI',
regex: '^http://.*',
}
const { regex, ...noRegex } = myObject
console.log(noRegex)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
I want to make an array based on two arrays - "ideaList" and "endorsements" declared globally. As ideaList and endorsements are used in other parts of the program I need them to be immutable, and I thought that .map and .filter would keep this immutability.
function prepareIdeaArray(){
var preFilteredIdeas=ideaList
.filter(hasIdeaPassedControl)
.map(obj => {obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
.reduce((sum, x)=>sum+x.count,0);
obj.like = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber && x.who===activeUser)
.reduce((sum, x)=>sum+x.count,0)===0?false:true
obj.position = generatePosition(obj.status)
obj.description = obj.description.replace(/\n/g, '<br>')
return obj;});
preFilteredIdeas.sort(compareOn.bind(null,'count',false)).sort(compareOn.bind(null,'position',true))
return preFilteredIdeas;
}
However, when I console.log ideaList after this function has been executed, I remark that objects of the array all have the "count", "like", "position" properties with values, which proves that the array has been mutated.
I tried by using .map only, but same result.
Would you know how I could prevent ideaList to get mutated? Also I would like to avoid to use const, as I declare ideaList globally first, and then assign to it some data in another function.
You're not mutating the array itself but rather the objects that the array contains references to. .map() creates a copy of the array but the references contained in it points to the exact same objects as the original, which you've mutated by adding properties directly to them.
You need to make copies of these objects too and add the properties to these copies. A neat way to do this is to use object spread in .map() callback:
.map(({ ...obj }) => {
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
If your environment doesn't support object spread syntax, clone the object with Object.assign():
.map(originalObj => {
const obj = Object.assign({}, originalObj);
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
In JS, the objects are referenced. When created, in other words, you get the object variable to point to a memory location which intends to be holding a meaningful value.
var o = {foo: 'bar'}
The variable o is now point to a memory which has {foo: bar}.
var p = o;
Now the variable p too is pointing to the same memory location. So, if you change o, it will change p too.
This is what happens inside your function. Even though you use Array methods which wouldn't mutate it's values, the array elements themselves are objects which are being modified inside the functions. It creates a new array - but the elements are pointing to the same old memory locations of the objects.
var a = [{foo: 1}]; //Let's create an array
//Now create another array out of it
var b = a.map(o => {
o.foo = 2;
return o;
})
console.log(a); //{foo: 2}
One way out is to create a new object for your new array during the operation. This can be done with Object.assign or latest spread operator.
a = [{foo: 1}];
b = a.map(o => {
var p = {...o}; //Create a new object
p.foo = 2;
return p;
})
console.log(a); // {foo:1}
To help having immutability in mind you could think of your values as primitives.
1 === 2 // false
'hello' === 'world' // false
you could extend this way of thinking to non-primitives as well
[1, 2, 3] === [1, 2, 3] // false
{ username: 'hitmands' } === { username: 'hitmands' } // false
to better understand it, please have a look at MDN - Equality Comparisons and Sameness
how to force immutability?
By always returning a new instance of the given object!
Let's say we have to set the property status of a todo. In the old way we would just do:
todo.status = 'new status';
but, we could force immutability by simply copying the given object and returning a new one.
const todo = { id: 'foo', status: 'pending' };
const newTodo = Object.assign({}, todo, { status: 'completed' });
todo === newTodo // false;
todo.status // 'pending'
newTodo.status // 'completed'
coming back to your example, instead of doing obj.count = ..., we would just do:
Object.assign({}, obj, { count: ... })
// or
({ ...obj, count: /* something */ })
there are libraries that help you with the immutable pattern:
Immer
ImmutableJS
You use the freeze method supplying the object you want to make immutable.
const person = { name: "Bob", age: 26 }
Object.freeze(person)
You could use the new ES6 built-in immutability mechanisms, or you could just wrap a nice getter around your objects similar to this
var myProvider = {}
function (context)
{
function initializeMyObject()
{
return 5;
}
var myImmutableObject = initializeMyObject();
context.getImmutableObject = function()
{
// make an in-depth copy of your object.
var x = myImmutableObject
return x;
}
}(myProvider);
var x = myProvider.getImmutableObject();
This will keep your object enclosed outside of global scope, but the getter will be accessible in your global scope.
You can read more on this coding pattern here
One easy way to make "copies" of mutable objects is to stringify them into another object and then parse them back into a new array. This works for me.
function returnCopy(arrayThing) {
let str = JSON.stringify(arrayThing);
let arr = JSON.parse(str);
return arr;
}
Actually, you can use spread opreator to make the original array stay unchanged, below y is the immutable array example:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = [...y]; // make array y as immutable array [1,2,3,4,5]
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
If you don't use the spread operator, the y array will get changed when loop is running time by time.
Here is the mutable array result:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = y; // this is mutable, because arr and y has same memory address
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
You assign these properties in your map function, you need to change this. (Just declare an empty object instead of using your current obj)
I am going through some old code from a defunct developer and noticed that sometimes he used
Object.assign({}, xyz)
and others he used
Object.assign([], abc);
Is there a difference between the two?
Yes, there is a difference. One assigns the values to a new object and the other assigns the values to a new array.
Look at the output here and compare to the output in the actual browser console.
var abc = {foo:"bar"};
var r1 = Object.assign({},abc);
var r2 = Object.assign([],abc);
console.log(r1);
console.log(r2);
What the second one doesn't do is add a new item to the array - it still has a length of 0.
Object.assign is a function that copies the enumerable own properties from one object to another (called target) and returns the target object. Since arrays are also objects you can copy properties to them as well, however, they won't show up during iteration.
Take the following snippet for example:
obj is an object
arr is an object, but also an array
arr.a is 1
arr.forEach does not print a
const obj = Object.assign({}, {
a: 1,
0: 2
});
const arr = Object.assign([], {
a: 1,
0: 2
});
console.log(typeof obj);
console.log(typeof arr);
console.log(Array.isArray(arr));
console.log(obj);
console.log(arr);
console.log(Object.keys(arr));
console.log(arr.a);
arr.forEach((item, idx) => console.log(`${idx}: ${item}`));
// To test the question in the comments
let copiedObj = { a: 123 };
let copy = Object.assign({}, copiedObj);
console.log(copy);
copiedObj = [];
console.log(copiedObj);
console.log(copy);
If I have a JS object, and I'd like to create a new object which copies over all properties except for a blacklist of properties that need to be filtered out, what's the simplest way to do it?
So I would do something like
originalObject.copyAndFilter('a', 'b')
Which would take the original object, copy it, and make sure properties a and b aren't in the new object.
I'm using ES2015/2016 through Babel so if it provides an even simpler way that would work too.
You could use Set and delete the unwanted properties.
var object = { a: 1, b: 2, c: 3, d: 4 },
filteredObject = {},
p = new Set(Object.keys(object));
blacklist = ['a', 'b'];
blacklist.forEach(a => p.delete(a));
[...p].forEach(k => filteredObject[k] = object[k]);
console.log(filteredObject);
Well, there's no simple native way to do it, I would convert the keys to an array, filter it, then create a new object:
const obj = {a: 'foo', b: 'bar', c: 'baz'};
const blacklist = ['a', 'b'];
const keys = Object.keys(obj);
const filteredKeys = keys.filter(key => !blacklist.includes(key));
const filteredObj = filteredKeys.reduce((result, key) => {
result[key] = obj[key];
return result;
}, {});
console.log(filteredObj);
You can just loop over object keys and create a new object. You can even use for...in for it. This will have added advantage of being supported in all browsers.
var obj = {a: 'foo', b: 'bar', c: 'baz'};
var blackListKeys = ['a', 'b','z'];
var obj2 = {}
for(var k in obj){
if(blackListKeys.indexOf(k) === -1)
obj2[k] = obj[k];
}
console.log(obj2)
var obj = {foo: 1, a: 2, b:3}
var newObj = {}
var blacklist = ['a', 'b']
for(let [key, value] of Object.entries(obj)) {
!blacklist.includes(key) && (newObj[key] = value)
}
console.log(newObj)
With fewer variables:
var obj = {foo: 1, a: 2, b: 3}
var newObj = Object.entries(obj)
.filter(([key, val]) => !['a', 'b'].includes(key))
.reduce((newObj, [key, value]) => (newObj[key] = value, newObj), {})
console.log(newObj)
Here there are two problems:
First one is actually copying the object without references.
If you simply loop against object keys and perform assignments of its values from the original object, if that values are also objects, you end up referencing that objects (or even arrays), not copying itself. So you will need to detect that and recursively call your copy_object function to avoid references.
Fortunately that problem is already solved in some libraries which provides an object extend() function like npm (server side) and jQuery (browser).
The trick consist only on extending new freshly created object:
var dst_object = extend({}, src_object);
The second problem is to avoid undesired keys.
Here there are to possible approaches: One is to fully reimplement beforementioned extend() function in a way that they provide a blacklist functionality.
...and the second (and less error prone) is to simply drop undesired keys after copying the object. If they aren't huge data structures, the overhead will be insignificant.
Example:
// npm install --save extend
var extend = require("extend");
function oClone(target, blacklist) {
var dest = extend({}, target);
if (blacklist) {
for (var i=0; i<blacklist.length; i++) {
delete (dest[blacklist[i]]);
};
};
return dest;
};
I think Object.assign is the best ES5 utility to make this with little extra logic:
function copyAndFilter(...args){
var copy = Object.assign( {} , this);
args.forEach(prop => {
delete copy[prop];
}); return copy;
}
Then:
var a = {foo : 'foo' , bar : 'bar'};
a.copyAndFilter = /* function definition above*/
a.copyAndFilter('foo');// { bar : 'bar' }
This sounds like a simple task, but I can't quite figure it out: I have an array :
var array = ['opt1','sub1','subsub1','subsubsub1']
From that I want to generate the following objects:
{
opt1:{
sub1:{
subsub1:{
subsubsub1:{}
}
}
}
}
I have a way to do it, making a string and using eval, but I'm looking to avoid that, any idea?
You could use reduce:
var array = ['opt1','sub1','subsub1','subsubsub1'];
var object = {};
array.reduce(function(o, s) { return o[s] = {}; }, object);
console.log(object);
But this was only introduced in ECMAScript 5.1, so it won't be supported in some older browsers. If you want something that will be supported by legacy browsers, you could use the polyfill technique described in the MDN article above, or a simple for-loop, like this:
var array = ['opt1','sub1','subsub1','subsubsub1'];
var object = {}, o = object;
for(var i = 0; i < array.length; i++) {
o = o[array[i]] = {};
}
console.log(object);
You can use reduceRight to transform the array into a 'chain' of objects:
const array = ['a', 'b', 'c'];
const object = array.reduceRight((obj, next) => ({[next]: obj}), {});
// Example:
console.log(object); // {"a":{"b":{"c":{}}}}
you could use lodash set function
_.set(yourObject, 'a.b.c')
You can use the following Function
function arr2nestedObject(myArray){
var cp_myArray = myArray;
var lastobj = {};
while(cp_myArray.length>0){
newobj = {};
var prop = cp_myArray.pop();
newobj[prop] = lastobj;
lastobj = newobj;
}
return lastobj;
}
The following code:
var myArray = ["personal-information", "address", "street",'Great-Success'];
console.log(JSON.stringify(arr2nestedObject(myArray),undefined,2));
Would Produce the Following Output:
{
"personal-information": {
"address": {
"street": {
"Great-Success": {}
}
}
}
}
Please let me know if that was what you meant.
Kind Regards.
As #p.s.w.g answer is a very good answer with pure js, but if you want an alternative with in a descriptive and functional way of that and set a value for final nested prop, you can use ramdajs assocPath https://ramdajs.com/docs/#assocPath like below:
var array = ['opt1','sub1','subsub1','subsubsub1'];
R.assocPath(array, "the value", {});
more details:
Makes a shallow clone of an object, setting or overriding the nodes
required to create the given path, and placing the specific value at
the tail end of that path. Note that this copies and flattens
prototype properties onto the new object as well. All non-primitive
properties are copied by reference.
examples:
R.assocPath(['a', 'b', 'c'], 42, {a: {b: {c: 0}}}); //=> {a: {b: {c: 42}}}
// Any missing or non-object keys in path will be overridden
R.assocPath(['a', 'b', 'c'], 42, {a: 5}); //=> {a: {b: {c: 42}}}