Copying objects in js (need guidance on pass by reference from docs) - javascript

I looked in the js docs and while studying the doc it mentioned in object.assign()
If the source value is a reference to an object, it only copies the reference value.
In my below snippet, one alters the original object and the other doesn't
var objA = {
a: 1,
b: {
c: 2,
d: {
e: 3,
},
},
}
var objC = { t: 1 };
//why this works i.e adds a to objEmpty
// var objEmpty = Object.assign({}, { objC }); //this would add a prop to the objEmpty object
//this doesnt work i.e doesnt add a to objEmpty
var objEmpty = Object.assign({}, objC); //this will not
objC.a = 3;
console.log(objC);
console.log(objEmpty);
I am getting my head around how really things work by trying different scenarios and I believe that it is something related to reference but how?
Another example from the docs
const obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 0}}
console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 0}}
console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 3}}
console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 3}}```
why does js behave this way? why the 3 got changed but the other didn't?
Thanks in advance :)

why does js behave this way? why the 3 got changed but the other
didn't?
Because Object.assign() actually does Shallow Copy, you need to do Deep Copy
To do deep copy of an object.
There are bunch of ways, The most common and popular way is to use JSON.stringify() with JSON.parse().
const oldObj = {a: {b: 1}, c: 2};
const newObj = JSON.parse(JSON.stringify(oldObj));
oldObj.a.b = 3; // will not affect the newObj
console.log('oldObj', oldObj);
console.log('newObj', newObj);
Note: There's a new JS standard called structured cloning. It works on all browsers:
method creates a deep clone of a given value using the structured clone algorithm.
const clone = structuredClone(object);

Related

How to change values of every id in this nested object array?

I have data structure similar like this
{"id": 1, "a": [{"id": 2, "b": [{"id": 3, "c": [{"id": 4}]}]}]}
I would like to change every id to null.
I know I could do loops and manually assign null to id. Is there any easy way to do this in JavaScript?
Without changing the object to a JSON string, you could use recursion:
function clearIds(obj) {
if ("id" in Object(obj)) obj.id = null;
Object.values(Object(obj)).forEach(clearIds);
}
// Demo
let obj = {"id": 1, "a": [{"id": 2, "b": [{"id": 3, "c": [{"id": 4}]}]}]};
clearIds(obj);
console.log(obj);
This mutates the given object in place.
For fun: the same function as a one-liner:
const clearIds = o => "id" in Object(o) && (o.id = null) || Object.values(Object(o)).forEach(clearIds);
// Demo
let obj = {"id": 1, "a": [{"id": 2, "b": [{"id": 3, "c": [{"id": 4}]}]}]};
clearIds(obj);
console.log(obj);
Bravo's comment was illuminating so I hope they don't mind if I expand on it here.
Turn the object into a string.
Parse it back into an object again.
What I didn't know is that JSON.parse has a "reviver" function that allows you to transform the string value as it's being parsed.
const data = {"id": 1, "a": [{"id": 2, "b": [{"id": 3, "c": [{"id": 4}]}]}]};
// Turn the data into a string
const str = JSON.stringify(data);
// Parse the string back to an object
// but using the reviver function to turn the value of any id
// keys to null
const newObj = JSON.parse(str, (key, value) => key === 'id' ? null : value);
console.log(newObj);

How do I take all of an object's properties and insert them into its own object array property?

For example I want something like:
{
a: 1,
b: 2,
c: 3
}
turned into:
{
d: {
a: 1,
b: 2,
c: 3
}
}
I've tried assigning a new property to that object with the object itself but it shows up as circular so I figure it's a reference instead of the actual properties instead of the actual values. I want to try something like JSON.stringify the object and assign it to the property but I don't know how to turn that string into an object format that I can assign to the property.
let firstObj = {
a: 1,
b: 2,
c: 3
}
let secondObj = {};
secondObj.d = firstObj;
console.log(secondObj);
Basically you create a new object and assign the original object to its property d.
You can use ES6 destructuting to make a shallow copy of the object and put it on a new prop:
let obj = {
a: 1,
b: 2,
c: 3
}
obj.d = {...obj}
console.log(obj)
If that's not an option you can reduce() over the objects keys to make a new object and assign it to d:
let obj = {
a: 1,
b: 2,
c: 3
}
obj.d = Object.keys(obj).reduce((newObj, k) => {
newObj[k] = obj[k]
return newObj
},{})
console.log(obj)
It depends whether you want to make the deep or shallow copy of the object d. (Can the object d have a nested structure?)
The question about efficient ways to clone the object has already been answered here.

Javascript array includes

I'm trying to figure out if a array contains a specific index or not using the following codelines:
var array1 = [{ "abc": 123, "def": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 2, "c": 1}]}]
console.log(array1.includes('def'));
The array contains "def" so the console should actually return true if I define the array in the following way:
var array1 = [{ "abc": 123, "def": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 2, "c": 1}]}]
Defining it the other way, like:
var array1 = [{ "abc": 123 }]
should return false.
The code above therefore does not work correctly, does anyone have a idea whats causing it to respond a wrong boolean?
I appreciate any kind of suggestions!
The proper method would be array1.some(n => n.hasOwnProperty('def')). See that there is no def in array, but rather object that contains def property
Array.includes returns a boolean based on whether (x) is a value in the given Array, not keys. If you want array1.includes('def') to return the other parts then you must do Object.keys(array1[0]).includes('def')
That is because you do not indicate array you think of.
If you console.log
Object.getOwnPropertyNames(array1), array1 is an array of one object. To get proper result you have to check if the object inside have the def property, so:
array1[0].hasOwnProperty('def') // returns true
The problem here is you try to access an element that is in an object and this object is in a list. To acces the list element, you need to specify its index (here 0). Then you can access the object keys with Object.keys.
var array1 = [{ "abc": 123, "def": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 2, "c": 1}]}]
Object.keys(array1[0]).forEach((element) => {
if(element === "def") console.log(true);
})

How do I use an array of indices to alter a specific item in a multi-dimensional object of unknown depth? [duplicate]

This question already has answers here:
Access nested objects via array of property names
(2 answers)
Closed 6 years ago.
Imagine I have an array, which looks like:
const x = ["a", "b", "c", "d"]
I wish to use it in order to navigate an object that I don't know the structure of, like:
const y = {
"a": {
"b": {
"c": {
"d": "Value I require"
}
},
"f": ["g", "h"]
},
"e": null
}
However, my issue is that I don't know how deep y will be, or how many indices are in the array x. How do I do the following:
let someVariable = {"prefilled": "data"}
someVariable[x[0]][x[1]][x[2]][x[3]] = y[x[0]][x[1]][x[2]][x[3]]
In a way which is neither specific to the length of x, the depth of y (and also preferably isn't my current solution, which is a case statement upto a depth of 6)? For this simplified case, someVariable should hopefully look as follows:
{
"prefilled": "data",
"a": {
"b": {
"c": {
"d": "Value I require"
}
}
}
}
I would either use a tool like Lodash's _.get or implement something similar that can navigate an object:
let _ = require('lodash');
const x = ["a", "b", "c", "d"]
_.get(object, x.join('.'))
You could iterate the array and walk the object.
const getValue = (object, path) => path.reduce((o, k) => (o || {})[k], object),
y = { a: { b: { c: { d: "Value I require" } }, f: ["g", "h"] }, e: null },
x = ["a", "b", "c", "d"]
console.log(getValue(y, x));

Lodash reject get return object

var obj = {a: [], b: [1,2], c: [], d: [1]};
How do I get a non-empty array of objects like the following:
{b: [1,2], d: [1]}
You can do what you are after, using pickBy().
var result = _.pickBy(obj, function(val){
return val.length > 0;
});
Fiddle here: https://jsfiddle.net/W4QfJ/3160/
Note: Unlike filter() and reject(), this returns an object, keeping your original structure (rather than an array).
Another way to do this: _.omitBy(obj, _.isEmpty);
_.filter() is what you're looking for:
var obj = {a: [], b: [1,2], c: [], d: [1]};
console.log(_.filter(obj, function(o){ return o.length; }))
If you want to use _.reject() like in your title, you can do something like this:
_.reject({a: [], b: [1,2], c: [], d: [1]},function(o){
return o.length == 0
});
Right now, Lodash has a method called _.omit that does exactly what you need:
> const object = {a: 1, b: 2, c: 3, d: 4}
undefined
> _.omit(object, ['a', 'c'])
{ b: 2, d: 4 }

Categories