Array.splice duplicate edit and insert after , unexpected result - javascript

In the following example (https://jsfiddle.net/dhkzwxr1/2/) :
I try to understand the behaviour of splice
const arr = [];
arr[0] = {name:"Jani",age:24};
arr[1] = {name:"Hege",age:32};
arr[2] = {name:"Stale",age:52};
arr[3] = {name:"Kai Jim",age:42};
arr[4] = {name:"Borge",age:25};
arr.splice(1, 0, {name:"Lene",age:27});
console.log(arr);
Insert at
Behave as expected
const arr2 = [];
arr2[0] = {name:"Jani",age:24};
arr2[1] = {name:"Hege",age:32};
arr2[2] = {name:"Stale",age:52};
arr2[3] = {name:"Kai Jim",age:42};
arr2[4] = {name:"Borge",age:25};
const jani = arr2[0];
jani.age = 19
arr2.splice(1, 0, jani);
console.log(arr2);
Output :
Array(6)
0: {name: "Jani", age: 19}
1: {name: "Jani", age: 19}
2: {name: "Hege", age: 32}
3: {name: "Stale", age: 52}
4: {name: "Kai Jim", age: 42}
5: {name: "Borge", age: 25}
length: 6
Why arr2[0].age === 19 and not 24 ?

This is not an issue with slice. What happens is that objects in JavaScript, like all other non-primitive values, are passed by reference.
So even if you insert an object into array, and update it after that, the variable will have the updated value. I think the most important thing to understand is that jani variable in your case does not have an actual value of the object, but just a reference to it.

like said commentator above Because you're inserting the same element into the array. The object referenced by jani is in the array twice once you splice it in.
and you should create a new object, you can do it by method Object.assign()
easy fix
var jani = Object.assign({}, arr2[0]);
working example
https://jsfiddle.net/fdtgvcLw/
When you assign one variable to another, it's not that both those variables are now linked by reference; you're misunderstanding what "pass by reference" means here.
A variable holding an object does not "directly" hold an object. What it holds is a reference to an object. When you assign that reference from one variable to another, you're making a copy of that reference. Now both variables hold a reference to an object. Modifying the object through that reference changes it for both variables holding a reference to that object.
When you assign a new value to one of the variables, you're just modifying the value that variable holds. The variable now ceases to hold a reference to the object, and instead holds something else. The other variable still holds its reference to the original object, the assignment didn't influence it at all.
Let's visualize:
var objOne = {
x: 1,
y: 2
};
// objOne -> { x: 1, y: 2 }
var objTwo = objOne;
// objOne -> { x: 1, y: 2 } <- objTwo
objTwo.x = 2;
// objOne -> { x: 2, y: 2 } <- objTwo (update object via objTwo variable)
objTwo = {};
// objOne -> { x: 2, y: 2 }, objTwo -> {}
This is how pass-by-reference works. Here objTwo is referring to objOne so whatever you do with objTwo will also happen with objOne.

It is not about splice. its with you have assigning object, and changing it's value to 19.
Then you insert another record to the array.
If you comment the var jani = arr2[0]; and splice it like you have done it before.
It would work.

Related

set new value for array's value using destructure

i got two noob questions about destructure an array:
1st question: when destructuring an object, I can define a new value or a new key or both. On array, can I add a new value without add a new key?
const obj = {a: undefined, b:2};
const {a = 3, b} = obj;
console.log(a); // 3
I want to know if there is a version of this but with array instead.
2nd question: is it possible to do not provide a default value for objects? Considering that I think that it is not possible to change default values using destructure.
const obj = [1, {a: 1, b:2}, 3, 4];
const [, object, three, four] = obj;
console.log(object); //{a: 1, b:2}
In this example, object returns {a: 1, b:2} but I wanted it change the value instead. Is that possible?
thanks, regards.
You are confusing default values with mutation of values, and assignment of values to variables with mutation of objects. Below is a demo of the default value feature of destructuring, with comments to explain the behavior.
You will see here that in general, destructuring is not designed for mutation of objects, but for extraction of variables and values. And hopefully also get a feel for why it would be undesirable for mutation to be mixed in to it, even if it were possible.
const obj = [1, {a: 1, b:2, 99:'z'}, ,3, 4, {mutateme: 1}];
const [, {a=3,b=4,c=5}, object={a:7,b:7},three, four, object2] = obj;
// a prop has value=1, b has value=2, c is not defined use default value 5
console.log(a,b,c,object);
//object is empty use default value={a:7,b:7}
// obj is unchanged
console.log(obj)
// mutate object2={mutateme:1} by reference (like a pointer)
object2.mutateme=7
// {mutateme: 1=>7}
console.log(obj)
// example of how you could (sort of) mutate inside a destructuring statement
// computed property, obj[1]=obj[3]=99 returns 99,
// so extract property 99 to variable z and mutate object obj at index [1] and [3] to =99
// y will 99 now.
const [y1, {[obj[1]=obj[3]=99]:z},, y2 ] = obj
console.log(y1, z, y2)
// if something similar were built into destructuring syntax,
// can you imagine how confusing it could get, and cause of all kinds of unexpected behavior?

How do I create a new JavaScript array without destroying the original array?

I never seem to understand JavaScript fully. Please explain this to me.
I have an array of objects called arr1. In this simplified example, it has only two members; name and someValue. The array is sorted by name and the content looks like this:
Harry 1
Joe 2
Joe 3
Peter 4
I want to create another array called arr2. It should hold the unique names from arr1. If a duplicate is found, the someValue is added for that name. I basically want this as content in arr2:
Harry 1
Joe 5 <-- the two values from Joe in arr1 sums up to 5
Peter 4
So I came up with the script below. The resulting arr2 is exactly what I want.
However, the arr1 is also updated and that's not what I desire.
Why is this happening and how can I fix it?
var arr1 = [];
var arr2 = [];
arr1.push({name: 'Harry', someValue: 1});
arr1.push({name: 'Joe', someValue: 2});
arr1.push({name: 'Joe', someValue: 3});
arr1.push({name: 'Peter', someValue: 4});
let previousName = '';
for (var i=0;i<arr1.length;i++) {
if (arr1[i].name == previousName) {
arr2[arr2.length-1].someValue += arr1[i].someValue;
} else {
arr2.push(arr1[i]);
previousName = arr1[i].name;
}
}
/* Result arr1 is not ok:
0: Object { name: "Harry", someValue: 1 }
1: Object { name: "Joe", someValue: 5 } <-- I want the value 2 to be preserved
2: Object { name: "Joe", someValue: 3 }
3: Object { name: "Peter", someValue: 4 }
Result arr2 is ok:
0: Object { name: "Harry", someValue: 1 }
1: Object { name: "Joe", someValue: 5 }
2: Object { name: "Peter", someValue: 4 }
*/
This is because you are pushing the object instance from arr1 onto arr2, instead of a copy.
Instead, simply create a new instance of the object when pushing onto arr2 and you will correct the problem.
Replace this line (which pushes the instance from arr1 onto arr2:
arr2.push(arr1[i]);
With this line (which creates a new instance and pushes it onto arr2:
arr2.push({name: arr1[i].name, someValue: arr1[i].someValue});
Here's a working snippet showing this:
var arr1 = [];
var arr2 = [];
arr1.push({name: 'Harry', someValue: 1});
arr1.push({name: 'Joe', someValue: 2});
arr1.push({name: 'Joe', someValue: 3});
arr1.push({name: 'Peter', someValue: 4});
;
let previousName = '';
for (var i = 0; i < arr1.length; i++) {
if (arr1[i].name == previousName) {
arr2[arr2.length - 1].someValue += arr1[i].someValue;
} else {
arr2.push({name: arr1[i].name, someValue: arr1[i].someValue});
previousName = arr1[i].name;
}
}
console.log(arr1);
console.log(arr2);
It happens because arrays and objects are reference types. So when you are editing
a reference , then it means that all objects will be edited.
So try to use spread operator to create brand new object:
arr2.push({...arr1[i]});
or Object.assign:
arr2.push(Object.assign({}, arr1[i]));
Thing is when you modify those values, as they are references, mean that they are connected, they are pointing to the same memory location where it keeps the value. Creating a duplicate, but separate array a2 such that changing a1[k] will not cause a2[k] to match the modified a1[k] requires cloning.
Shallow copies can be done using a simple loop or slicing. JSON.parse and JSON.stringify creates deep copies.
This link will explain you deeper why and how to solve it.
How to clone an array in JavaScript

iterating and storing object equal to itself

Can someone help me in understanding the reason why someone would iterate when it's value is equal to it?
for example
let subStyle = {
width: 300,
height: 50,
borderRadius: 20,
backgroundColor: theme.primaryBlue
};
subStyle = {
...subStyle,
backgroundColor: theme.primaryWhite,
borderWidth: 2,
borderColor: '#707070'
}
i.e in the above code, what could be the reason for doing the same?
subStyle = {
...subStyle,
backgroundColor: theme.primaryWhite,
borderWidth: 2,
borderColor: '#707070'
}
They're copying the properties from the object subStyle refers to into a new object (then overwriting one of them and adding two more), then assigning that new object to the subStyle variable. This is quite standard when you aren't allowed to modify the original object because something else may be using it (React state, for instance).
Simpler example:
let o1 = {a: "ay", b: "bee"};
let o2 = o1; // Something else is using the same object
// We need to create a new, updated object without
// affecting others who have access to the current one
o1 = {...o1, b: "BEE", c: "see"};
// The original is unchanged
console.log(o2);
// The new one has the updates
console.log(o1);
That ...someObject within an object initializer is called property spread. It was added to JavaScript in ES2018.
I should note that it's not iteration in JavaScript terms (though of course it's similar) and it doesn't require that the object be iterable. (In constrast, ... in an array initializer is iteration and does require that what you use it on be iterable.)
Because let's say you want to update that object in an immutable way. The mutable way would be:
let x = {a:1, b:2};
x.a=123;
Immutable way, you create a new object, copy properties of old one using ... operator (in a shallow way), and also assign a new property (or overwrite existing one):
let x = {a:1, b:2};
let y = {...x, a:123} // We created a new object and copied properties from x to it, also update value of a
This way you get object with same contents as in the first example, but you didn't modify x.
Essentially for keeping the same values in the object.
When you do subStyle = { ... } you are overwriting the object.
If you do subStyle = { ...subStyle } you will copy all you had before inside subStyle and the rest that you add will be added along
An illustrative example:
let myObject = {
a: 'a',
b: 'b'
}
console.log('My object: ', myObject)
myObject = { ...myObject }
console.log('Copying my object with spread operator: ', myObject)
myObject = {
...myObject,
c: 'c',
d: 'd'
}
console.log('Adding new elements to my copied object with spread operator: ', myObject)
That is called Destructuring_assignment. It allows to unpack objects into bunch of variables.
Let subStyle1 be a new variable. That ... will allow to copy the properties from the subStyle to subStyle1
let subStyle = {
width: 300,
height: 50,
borderRadius: 20,
backgroundColor: 'theme.primaryBlue'
};
let subStyle1 = {
...subStyle,
backgroundColor: 'theme.primaryWhite',
borderWidth: 2,
borderColor: '#707070'
};
console.log(subStyle1)

Javascript array referencing an array position (not an element)

UPDATE:
Many asked why not using [arr[0], arr[1]]. The problem is I have to pass this array to a method, which I don't have access Angular Material Table. And I don't want to call the method over and over again.
I already processed the arr array and I don't want to process pointer array to reflect the new data, which I already know where it is.
The Nina Scholz answer seems to solve the problem.
Is there a way to use "pointers" like C in Javascript?
What I want to do is:
I have an array with objects
const arr = [
{prop: 3},
{prop: 4},
];
And I want to have an array to point to the positions of this array
const pointer = [arr[0], arr[1]]; // I want pointer to point to be an array containing the first and second elements of arr
This will get a reference to the {prop: 3} and {prop: 4} objects, which is not what I want, because, if I do:
arr.splice(0, 0, {prop: 1}); // arr => [{prop:1},{prop:3},{prop:4}]
console.log(pointer); // [{prop: 3},{prop: 4}]
As you can see, pointer holds a reference to the objects {prop:3} and {prop:4}.
How can I achieve pointer to hold reference to the position 0 of the array, instead of the object stored in it? So, on this example, pointer => [{prop:1},{prop:3}]?
I can't call pointer = [arr[0], arr[1]] all the time because arr will change constantly and asynchronously.
Is there a "reactive" way to handle arrays?
If your pointers are always to the same array, you can simply store the indexes.
const pointer = [0, 1];
Then you would use:
console.log(pointer.map(ptr => arr[ptr]));
If your pointers can point to different arrays, you can make the elements of pointer be objects that contain references to the array along with their indexes.
const pointer = [{a: arr, i: 0}, {a: arr1, i: 1}];
console.log(pointer.map(({a, i}) => a[i]));
Interesting aside: several decades ago I used a C implementation for Symbolics Lisp Machines. This is basically how it represented C pointers.
You could use a getter function and return the element of the actual object.
const arr = [{ prop: 3 }, { prop: 4 }];
const pointer = [];
Object.defineProperty(pointer, 0, { get() { return arr[0]; } });
Object.defineProperty(pointer, 1, { get() { return arr[1]; } });
arr.splice(0, 0, { prop: 1 });
console.log(pointer);
You can use a Proxy (not supported by IE) with a get trap:
const arr = [{ prop: 3 }, { prop: 4 }];
const pointer = new Proxy([], {
get(target, prop, receiver) {
// if the prop is a string that can be converted to a number
// return the corresponding value from the arr
if(typeof prop === 'string' && !isNaN(Number(prop))) return arr[target[prop]];
return Reflect.get(target, prop, receiver);
}
});
pointer.push(0, 1);
console.log(pointer);
arr.splice(0, 0, { prop: 1 });
console.log(pointer);

How to set a property of a prototype object for a single class [duplicate]

This question already has answers here:
Crockford's Prototypal inheritance - Issues with nested objects
(3 answers)
Closed 4 years ago.
Say I change the object prototype like so:
Object.prototype.test = {val: 5, abc: 8};
Then I change a property of test for Array:
Array.prototype.test.abc = 20;
Then if I print the base test variable:
console.log(Object.prototype.test); // {val: 5, abc: 20}
console.log(({}).test); // {val: 5, abc: 20}
console.log(([]).test); // {val: 5, abc: 20}
How do I still have arrays inherit val as 5, but have an abc value of 20 without affecting Object's prototype
In your example Array.protoype does not have its own test property. So when you try to access it with Array.prototype.test.abc = 20; it looks up the prototype chain and find the .test object on Object.prototype and sets its .abc value to 20.
You can give Array.prototype it's own property test with something like:
Object.prototype.test = {val: 5, abc: 8};
Array.prototype.test = Object.assign({}, Object.prototype.test)
Array.prototype.test.abc = 20;
console.log(({}).test.abc); // 8
console.log(([]).test.abc); // 20
You could also link the test object from Array to Object so properties not found on Array.prototype.test will defer up the chain to Object.prototype.test, although this starts to get confusing:
Object.prototype.test = {val: 5, abc: 8};
Array.prototype.test = Object.create(Object.prototype.test)
Array.prototype.test.abc = 20;
console.log(([]).test.abc); // shadows with it's own abc
Object.prototype.test.abc = 500 // changes in object have no effect
console.log(([]).test.abc); // still 20
console.log(([]).test.val); // no val on Array's test so it defers to object prototype
Object.prototype.test.val = 100 // changing object changes array
console.log(([]).test.val);
…not that I really recommend any of this beyond test and exploring the ecosystem.

Categories