I have next code:
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
Why doesn't the following code delete the value of the secondList variable?
let secondList = list.next.next;
list.next.next = null;
Shouldn't the secondList have reference to the same object to which we assigned null?
The value of list.next.next is a reference to an object.
let secondList = list.next.next; copies the reference to that object to secondList.
list.next.next = null replaces the original reference to the object with null.
It doesn't delete or modify the object itself. The value of secondList is unchanged. Since there remains a reference to the object (in secondList), the object is not garbage collected.
Related
I am trying to modify the property at a specific index in my Array of Objects, Somehow when I try to modify one value it modifies all the values occurring in the array of objects.
Can anyone know what I did wrong here? I don't want to return a new array and want to update the same array.
let options = [];
let va = {
stat: {
cde: null,
abc: null,
},
};
options.push(va);
options.push(va);
//accessing first element from index
options[0].stat['abc'] = 'test';
console.log(options);
Here you are pushing the same va object reference two times in an options array. Hence, it is updating the value in both the objects.
If you will try to log your options array you will get the [circular object Object].
If your objects will not contain the same reference, this will not be happening.
Demo :
let options = [];
let va = {
stat: {
cde: null,
abc: null,
},
};
let ba = {
stat: {
cde: null,
abc: null,
},
};
options.push(va);
options.push(ba);
//accessing first element from index
options[0].stat['abc'] = 'test';
console.log(options);
For the following code block:
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
items.forEach((item, i) => {
item = {
...item,
...changes
}
})
console.log(items) // items NOT reassigned with changes
items.forEach((item, i) => {
items[i] = {
...item,
...changes
}
});
console.log(items) // items reassigned with changes
Why does reassigning the values right on the element iteration not change the objects in the array?
item = {
...item,
...changes
}
but changing it by accessing it with the index does change the objects in the array?
items2[i] = {
...item,
...changes
}
And what is the best way to update objects in an array? Is items2[i] ideal?
Say no to param reassign!
This is a sort of a fundamental understanding of higher level languages like JavaScript.
Function parameters are temporary containers of a given value.
Hence any "reassigning" will not change the original value.
For example look at the example below.
let importantObject = {
hello: "world"
}
// We are just reassigning the function parameter
function tryUpdateObjectByParamReassign(parameter) {
parameter = {
...parameter,
updated: "object"
}
}
tryUpdateObjectByParamReassign(importantObject)
console.log("When tryUpdateObjectByParamReassign the object is not updated");
console.log(importantObject);
As you can see when you re-assign a parameter the original value will not be touched. There is even a nice Lint rule since this is a heavily bug prone area.
Mutation will work here, but ....
However if you "mutate" the variable this will work.
let importantObject = {
hello: "world"
}
// When we mutate the returned object since we are mutating the object the updates will be shown
function tryUpdateObjectByObjectMutation(parameter) {
parameter["updated"] = "object"
}
tryUpdateObjectByObjectMutation(importantObject)
console.log("When tryUpdateObjectByObjectMutation the object is updated");
console.log(importantObject);
So coming back to your code snippet. In a foreach loop what happens is a "function call" per each array item where the array item is passed in as a parameter. So similar to above what will work here is as mutation.
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
items.forEach((item, i) => {
// Object assign just copies an object into another object
Object.assign(item, changes);
})
console.log(items)
But, it's better to avoid mutation!
It's better not mutate since this can lead to even more bugs. A better approach would be to use map and get a brand new collection of objects.
const items = [{
id: 1,
name: 'one'
},
{
id: 2,
name: 'two'
},
];
const changes = {
name: 'hello'
}
const updatedItems = items.map((item, i) => {
return {
...item,
...changes
}
})
console.log({
items
})
console.log({
updatedItems
})
As the MDN page for forEach says:
forEach() executes the callbackFn function once for each array
element; unlike map() or reduce() it always returns the value
undefined and is not chainable. The typical use case is to execute
side effects at the end of a chain.
Have a look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
This means that although you did create new object for item, it was not returned as a value for that index of array. Unlike your second example, the first one is not changing original array, but just creates new objects and returns undefined. This is why your array is not modified.
I'd go with a classic Object.assign for this:
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
items.forEach( (item) => Object.assign(item,changes) )
console.log(items)
Properties in the target object are overwritten by properties in the sources if they have the same key. Later sources' properties overwrite earlier ones.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
The other approach you can take is to use map and create a new array based on the original data and the changes:
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
const newItems = items.map((item) => {
...item,
...changes
})
console.log(newItems);
But if you need to modify the original array, it's either accessing the elements by index, or Object.assign. Attempting to assign the value directly using the = operator doesn't work because the item argument is passed to the callback by value not by reference - you're not updating the object the array is pointing at.
I have a function that accepts the following arguments:
set(section, field, pair, component, element, value)
The section, field, pair and component are just keys within the Object. They are way-points so we can travel down the hierarchy. Obviously section is the head, our entry point.
element is the target key and the value is the value that will be set.
Since, there are elements at different depths, I would like to do the following:
set('utility', null, null, null, 'exportId', 'banana')
This is for a shallow access, and internally it will do this:
dataObj[section][element] = value;
**/ As in
* data: {
utility: {
exportId: 'banana'
}
* }
*/
In other cases, when the element is deeper inside the Object, it may be required to do the following:
dataObj[section][field][pair][component][element] = value;
What would be the best way, to define the path to the element dynamically, so we skip the keys that are passed in as a 'null'?
for example:
set('history', 'current', null, null, 'fruit', 'apple')
**/ As in
* data: {
history: {
current: {
fruit: 'apple'
}
}
* }
*/
will internally be constructed as:
dataObj[section][field][element] = value;
as you might have noticed, we skipped [pair][component] because those slots were passed in as null(s).
Instead of having a long list of specific parameters, just pass an object to the function. this way you only pass what you need to and there won't be any "null" references to deal with on the call.
Using this implementation, this call can be shortened to something like this:
Your current implementation:
set('utility', 'current', null, null, 'exportId', 'banana')
Using an object as the parameter:
set({
section:'utility',
field:'current',
element: 'exportId',
value:'banana'
});
You could use rest parameters to get the arguments passed to an array. Then create an object using reduceRight
function set(...paths) {
return paths.reduceRight((r, key, i) => key !== null ? { [key] : r } : r)
}
console.log(set('history', 'current', null, null, 'fruit', 'apple'))
console.log(set('utility', null, null, null, 'exportId', 'banana'))
The above function will construct a nested object based on the paths. If you want to just update an existing object, you could traverse the object and set the value like this:
function set(dataObj, ...paths) {
let value = paths.pop(),
nested = dataObj;
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
if (i === paths.length - 1 && nested)
nested[path] = value; // set the value if it's the final item
if (path !== null && nested)
nested = nested[path]; // get another level of nesting
}
return dataObj;
}
let obj = { utility: { exportId: 'banana' } }
console.log(set(obj, 'utility', null, null, null, 'exportId', 'orange'))
obj = {
history: {
current: {
fruit: 'apple'
}
}
}
console.log(set(obj, 'history', 'current', null, null, 'fruit', 'orange'))
As per title, I need to clone an object in Javascript like that below and set each values to zero. Of course the object properties can change.
{ _id: { action: null, date: null },
avg: null,
min: null,
max: null,
total: null }
// helper method to get the correct object type
function toType(x) {
return ({}).toString.call(x).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
// recursive function that sets all properties to null
// except objects which it passes back into the reset function
function reset(obj) {
// clone the object
var out = JSON.parse(JSON.stringify(obj));
for (var p in out) {
if (toType(out[p]) === 'object') {
reset(out[p]);
} else {
out[p] = null;
}
}
return out;
}
reset(obj);
DEMO
Iam trying to push in array an object, but I get always error.
fCElements = [],
obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj,
myid:2,
name:'klaus'
})
how I can push into array functions like "myFunction"?
Thanks
In the Object literal, you can only give key-value pairs. Your obj doesn't have any value.
Instead, you can do like this
var fCElements = [];
fCElements.push({
obj: {
fun: myFunction,
id: 2
},
myid: 2,
name: 'klaus'
});
Now, you are creating a new object, obj, on the fly, while pushing to the array. Now, your fCElements look like this
[ { obj: { fun: [Function], id: 2 }, myid: 2, name: 'klaus' } ]
You need to give your obj property a name (or a value).
var obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj:obj,
myid:2,
name:'klaus'
});
The object you are pushing to the array seems off. It will try to push this object:
{
{fun: myfunction, id: 2},
myid: 2,
name: 'klaus'
}
Which is an invalid object since the first value has no key. You should do it like this instead:
fCElements.push ({
myObj:obj,
myid:2,
name:'klaus'
});