Is there a difference in Object.assign {} and Object.Assign [] - javascript

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);

Related

can someone please explain how the following code changes the value of "xs"? it should remain the same [duplicate]

When copying an array in JavaScript to another array:
var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d'); //Now, arr1 = ['a','b','c','d']
I realized that arr2 refers to the same array as arr1, rather than a new, independent array. How can I copy the array to get two independent arrays?
Use this:
let oldArray = [1, 2, 3, 4, 5];
let newArray = oldArray.slice();
console.log({newArray});
Basically, the slice() operation clones the array and returns a reference to a new array.
Also note that:
For references, strings and numbers (and not the actual object), slice() copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays.
Primitives such as strings and numbers are immutable, so changes to the string or number are impossible.
In Javascript, deep-copy techniques depend on the elements in an array. Let's start there.
Three types of elements
Elements can be: literal values, literal structures, or prototypes.
// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';
// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};
// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}
From these elements we can create three types of arrays.
// 1) Array of literal-values (boolean, number, string)
const type1 = [ true, 1, "true" ];
// 2) Array of literal-structures (array, object)
const type2 = [ [], {} ];
// 3) Array of prototype-objects (function)
const type3 = [ function () {}, function () {} ];
Deep copy techniques depend on the three array types
Based on the types of elements in the array, we can use various techniques to deep copy.
Deep copy techniques
Benchmarks
https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison
Array of literal-values (type1)
The [ ...myArray ], myArray.splice(0), myArray.slice(), and myArray.concat() techniques can be used to deep copy arrays with literal values (boolean, number, and string) only; where slice() has the highest performance in Chrome, and spread ... has the highest performance in Firefox.
Array of literal-values (type1) and literal-structures (type2)
The JSON.parse(JSON.stringify(myArray)) technique can be used to deep copy literal values (boolean, number, string) and literal structures (array, object), but not prototype objects.
All arrays (type1, type2, type3)
The Lo-dash cloneDeep(myArray) or jQuery extend(true, [], myArray) techniques can be used to deep-copy all array-types. Where the Lodash cloneDeep() technique has the highest performance.
And for those who avoid third-party libraries, the custom function below will deep-copy all array-types, with lower performance than cloneDeep() and higher performance than extend(true).
function copy(aObject) {
// Prevent undefined objects
// if (!aObject) return aObject;
let bObject = Array.isArray(aObject) ? [] : {};
let value;
for (const key in aObject) {
// Prevent self-references to parent object
// if (Object.is(aObject[key], aObject)) continue;
value = aObject[key];
bObject[key] = (typeof value === "object") ? copy(value) : value;
}
return bObject;
}
So to answer the question...
Question
var arr1 = ['a','b','c'];
var arr2 = arr1;
I realized that arr2 refers to the same array as arr1, rather than a new, independent array. How can I copy the array to get two independent arrays?
Answer
Because arr1 is an array of literal values (boolean, number, or string), you can use any deep copy technique discussed above, where slice() and spread ... have the highest performance.
arr2 = arr1.slice();
arr2 = [...arr1];
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = copy(arr1); // Custom function needed, and provided above
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = jQuery.extend(true, [], arr1); // jQuery.js needed
You can use array spreads ... to copy arrays.
const itemsCopy = [...items];
Also if want to create a new array with the existing one being part of it:
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
Array spreads are now supported in all major browsers but if you need older support use typescript or babel and compile to ES5.
More info on spreads
No jQuery needed... Working Example
var arr2 = arr1.slice()
This copys the array from the starting position 0 through the end of the array.
It is important to note that it will work as expected for primitive types (string, number, etc.), and to also explain the expected behavior for reference types...
If you have an array of Reference types, say of type Object. The array will be copied, but both of the arrays will contain references to the same Object's. So in this case it would seem like the array is copied by reference even though the array is actually copied.
This is how I've done it after trying many approaches:
var newArray = JSON.parse(JSON.stringify(orgArray));
This will create a new deep copy not related to the first one (not a shallow copy).
Also this obviously will not clone events and functions, but the good thing you can do it in one line, and it can be used for any kind of object (arrays, strings, numbers, objects ...)
An alternative to slice is concat, which can be used in 2 ways. The first of these is perhaps more readable as the intended behaviour is very clear:
var array2 = [].concat(array1);
The second method is:
var array2 = array1.concat();
Cohen (in the comments) pointed out that this latter method has better performance.
The way this works is that the concat method creates a new array consisting of the elements in the object on which it is called followed by the elements of any arrays passed to it as arguments. So when no arguments are passed, it simply copies the array.
Lee Penkman, also in the comments, points out that if there's a chance array1 is undefined, you can return an empty array as follows:
var array2 = [].concat(array1 || []);
Or, for the second method:
var array2 = (array1 || []).concat();
Note that you can also do this with slice: var array2 = (array1 || []).slice();.
Important!
Most of answers here works for particular cases.
If you don't care about deep/nested objects and props use (ES6):
let clonedArray = [...array]
but if you want to do deep clone use this instead:
let cloneArray = JSON.parse(JSON.stringify(array))*
*functions won't be preserved (serialized) while using stringify, you will get result without them.
For lodash users:
let clonedArray = _.clone(array) documentation
and
let clonedArray = _.cloneDeep(array) documentation
I personally think Array.from is a more readable solution. By the way, just beware of its browser support.
// clone
let x = [1, 2, 3];
let y = Array.from(x);
console.log({y});
// deep clone
let clone = arr => Array.from(arr, item => Array.isArray(item) ? clone(item) : item);
x = [1, [], [[]]];
y = clone(x);
console.log({y});
Some of mentioned methods work well when working with simple data types like number or string, but when the array contains other objects these methods fail. When we try to pass any object from one array to another it is passed as a reference, not the object.
Add the following code in your JavaScript file:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (i in this) {
if (i == 'clone')
continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
}
else
newObj[i] = this[i]
} return newObj;
};
And simply use
var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()
It will work.
From ES2015,
var arr2 = [...arr1];
If you are in an environment of ECMAScript 6, using the Spread Operator you could do it this way:
var arr1 = ['a','b','c'];
var arr2 = [...arr1]; //copy arr1
arr2.push('d');
console.log(arr1)
console.log(arr2)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>
Primitive values are always pass by its value (copied). Compound values however are passed by reference.
So how do we copy this arr?
let arr = [1,2,3,4,5];
Copy an Array in ES6
let arrCopy = [...arr];
Copy n Array in ES5
let arrCopy = arr.slice();
let arrCopy = [].concat(arr);
Why `let arrCopy = arr` is not passing by value?
Passing one varible to another on Compound values such as Object/Array behave difrently. Using asign operator on copand values we pass reference to an object. This is why the value of both arrays are changing when removing/adding arr elements.
Exceptions:
arrCopy[1] = 'adding new value this way will unreference';
When you assign a new value to the variable, you are changing the reference itself and it doesn’t affect the original Object/Array.
read more
Adding to the solution of array.slice(); be aware that if you have multidimensional array sub-arrays will be copied by references.
What you can do is to loop and slice() each sub-array individually
var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();
arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);
function arrCpy(arrSrc, arrDis){
for(elm in arrSrc){
arrDis.push(arrSrc[elm].slice());
}
}
var arr3=[];
arrCpy(arr,arr3);
arr3[1][1] = 77;
console.log(arr3[1][1]);
console.log(arr[1][1]);
same things goes to array of objects, they will be copied by reference, you have to copy them manually
let a = [1,2,3];
Now you can do any one of the following to make a copy of an array.
let b = Array.from(a);
OR
let b = [...a];
OR
let b = new Array(...a);
OR
let b = a.slice();
OR
let b = a.map(e => e);
Now, if i change a,
a.push(5);
Then, a is [1,2,3,5] but b is still [1,2,3] as it has different reference.
But i think, in all the methods above Array.from is better and made mainly to copy an array.
I would personally prefer this way:
JSON.parse(JSON.stringify( originalObject ));
You must use best practice for this question when there are a lot of answers.
I recommend to you use array spreads … to copy arrays.
var arr1 = ['a','b','c'];
var arr2 = […arr1];
As we know in Javascript arrays and objects are by reference, but what ways we can do copy the array without changing the original array later one?
Here are few ways to do it:
Imagine we have this array in your code:
var arr = [1, 2, 3, 4, 5];
1) Looping through the array in a function and return a new array, like this:
function newArr(arr) {
var i=0, res = [];
while(i<arr.length){
res.push(arr[i]);
i++;
}
return res;
}
2) Using slice method, slice is for slicing part of the array, it will slice some part of your array without touching the original, in the slice, if don't specify the start and end of the array, it will slice the whole array and basically make a full copy of the array, so we can easily say:
var arr2 = arr.slice(); // make a copy of the original array
3) Also contact method, this is for merging two array, but we can just specify one of arrays and then this basically make a copy of the values in the new contacted array:
var arr2 = arr.concat();
4) Also stringify and parse method, it's not recommended, but can be an easy way to copy Array and Objects:
var arr2 = JSON.parse(JSON.stringify(arr));
5) Array.from method, this is not widely supported, before use check the support in different browsers:
const arr2 = Array.from(arr);
6) ECMA6 way, also not fully supported, but babelJs can help you if you want to transpile:
const arr2 = [...arr];
Dan, no need to use fancy tricks. All you need to do is make copy of arr1 by doing this.
var arr1 = ['a','b','c'];
var arr2 = [];
var arr2 = new Array(arr1);
arr2.push('d'); // Now, arr2 = [['a','b','c'],'d']
console.log('arr1:');
console.log(arr1);
console.log('arr2:');
console.log(arr2);
// Following did the trick:
var arr3 = [...arr1];
arr3.push('d'); // Now, arr3 = ['a','b','c','d'];
console.log('arr3:');
console.log(arr3);
Now arr1 and arr2 are two different array variables stored in separate stacks.
Check this out on jsfiddle.
I found this method comparatively easier:
let arr = [1,2,3,4,5];
let newArr = [...arr];
console.log(newArr);
In my particular case I needed to ensure the array remained intact so this worked for me:
// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
arr1.push(arr2[i]);
}
Make copy of multidimensional array/object:
function deepCopy(obj) {
if (Object.prototype.toString.call(obj) === '[object Array]') {
var out = [], i = 0, len = obj.length;
for ( ; i < len; i++ ) {
out[i] = arguments.callee(obj[i]);
}
return out;
}
if (typeof obj === 'object') {
var out = {}, i;
for ( i in obj ) {
out[i] = arguments.callee(obj[i]);
}
return out;
}
return obj;
}
Thanks to James Padolsey for this function.
Source: Here
If your array contains elements of the primitive data type such as int, char, or string etc then you can user one of those methods which returns a copy of the original array such as .slice() or .map() or spread operator(thanks to ES6).
new_array = old_array.slice()
or
new_array = old_array.map((elem) => elem)
or
const new_array = new Array(...old_array);
BUT if your array contains complex elements such as objects(or arrays) or more nested objects, then, you will have to make sure that you are making a copy of all the elements from the top level to the last level else reference of the inner objects will be used and that means changing values in object_elements in new_array will still affect the old_array. You can call this method of copying at each level as making a DEEP COPY
of the old_array.
For deep copying, you can use the above-mentioned methods for primitive data types at each level depending upon the type of data or you can use this costly method(mentioned below) for making a deep copy without doing much work.
var new_array = JSON.parse(JSON.stringify(old_array));
There are a lot of other methods out there which you can use depending on your requirements. I have mentioned only some of those for giving a general idea of what happens when we try to copy an array into the other by value.
If you want to make a new copy of an object or array, you must explicitly copy the properties of the object or the elements of the array, for example:
var arr1 = ['a','b','c'];
var arr2 = [];
for (var i=0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
You can search for more information on Google about immutable primitive values and mutable object references.
You could use ES6 with spread Opeartor, its simpler.
arr2 = [...arr1];
There are limitations..check docs Spread syntax # mozilla
When we want to copy an array using the assignment operator ( = ) it doesn't create a copy it merely copies the pointer/reference to the array. For example:
const oldArr = [1,2,3];
const newArr = oldArr; // now oldArr points to the same place in memory
console.log(oldArr === newArr); // Points to the same place in memory thus is true
const copy = [1,2,3];
console.log(copy === newArr); // Doesn't point to the same place in memory and thus is false
Often when we transform data we want to keep our initial datastructure (e.g. Array) intact. We do this by making a exact copy of our array so this one can be transformed while the initial one stays intact.
Ways of copying an array:
const oldArr = [1,2,3];
// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];
// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();
// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);
// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();
// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);
// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);
// For loop
function clone(base) {
const newArray = [];
for(let i= 0; i < base.length; i++) {
newArray[i] = base[i];
}
return newArray;
}
const newArr7 = clone(oldArr);
console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);
Be careful when arrays or objects are nested!:
When arrays are nested the values are copied by reference. Here is an example of how this could lead to issues:
let arr1 = [1,2,[1,2,3]]
let arr2 = [...arr1];
arr2[2][0] = 5; // we change arr2
console.log(arr1); // arr1 is also changed because the array inside arr1 was copied by reference
So don't use these methods when there are objects or arrays inside your array you want to copy. i.e. Use these methods on arrays of primitives only.
If you do want to deepclone a javascript array use JSON.parse in conjunction with JSON.stringify, like this:
let arr1 = [1,2,[1,2,3]]
let arr2 = JSON.parse(JSON.stringify(arr1)) ;
arr2[2][0] = 5;
console.log(arr1); // now I'm not modified because I'm a deep clone
Performance of copying:
So which one do we choose for optimal performance. It turns out that the most verbose method, the for loop has the highest performance. Use the for loop for really CPU intensive copying (large/many arrays).
After that the .slice() method also has decent performance and is also less verbose and easier for the programmer to implement. I suggest to use .slice() for your everyday copying of arrays which aren't very CPU intensive. Also avoid using the JSON.parse(JSON.stringify(arr)) (lots of overhead) if no deep clone is required and performance is an issue.
Source performance test
var arr2 = arr1.slice(0);
This way just work for simple Arrays.
If you have Complex Array like array of Objects then you must use another solutions like:
const arr2 = JSON.parse(JSON.stringify(arr1));
For example, we have an array of objects that each cell have another array field in its object ... in this situation if we use slice method then the array fields will copy by Ref and that's mean these fields updates will affect on orginal array same element and fields.
Using jQuery deep copy could be made as following:
var arr2 = $.extend(true, [], arr1);
You can also use ES6 spread operator to copy Array
var arr=[2,3,4,5];
var copyArr=[...arr];
Here are few more way to copy:
const array = [1,2,3,4];
const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );
For ES6 array containing objects
cloneArray(arr) {
return arr.map(x => ({ ...x }));
}

How to keep an array with objects immutable in javascript?

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)

Why does changing the value of a property change the value for all similarly named properties in an array?

Why does changing the value of a property change the value for all similar properties in an array and how do I get it to work right without using the this keyword for 'name'?
let Object = {
'name' : 'Test Object'
}
let Array = []
Array.push(Object)
Array.push(Object)
Array.push(Object)
Array[0]['name'] = 'Changed'
console.log(Array) // expect only the first name to change, but all 3 change...
You aren't changing "similarly named" objects, you are changing the same object.
For non-primitives (basically everything that isn't a string, number, or boolean), they are passed by reference. That means when you add them to something like an array or pass them to a function, you are basically passing their address. If you pass it 3 times, they all point to the same address; there is still only one copy. Change one, and you change them all.
const a = { b: 1 };
const arr = [a, a, a];
// All the same object
console.log(arr[0] === arr[1], arr[1] === arr[2], a === arr[0]);
a.b = 5;
// All 3 changed, because it is the same thing
console.log(arr.map(a => a.b));
function someFunc(obj) { obj.b = 10 };
someFunc(a);
// changed from inside function, same object
console.log(a.b);
If you want to create a handful of objects that all start the same, but then are able to change afterwards, you need to create the objects in a loop:
const template = { name: 'a' };
const arr = [];
for (let i = 0; i < 3; i++) {
arr.push({ ...template }); // or: arr.push(Object.create({}, template))
}
arr[1].name = 'b';
arr[2].name = 'c';
console.log(arr);
Or, even more concisely:
// Creates a new Array with 3 records and then puts a copy of the template in each.
const template = { name: 'a' };
const arr = new Array(3).fill(1).map(() => ({ ...template }));
// or (without needing template variable):
// const arr = new Array(3).fill(1).map(() => ({ name: 'a' }))
arr[1].name = 'b';
arr[2].name = 'c';
console.log(arr);
When you invoke Array.push(Object), you're pushing a reference to the same object into the array 3 times. Push does not make a copy of Object - only 1 Object exists.
If you want 3 identical objects in an array, try something like this:
let vArray = []
for(i = 0; i <= 2; i++) {
//We're going to loop this 3 times, creating 3 different
//objects and pushing each of them into the array
let vObject = {
'name' : 'Test Object'
}
vArray.push(vObject)
}
vArray[0]['name'] = 'Changed'
console.log(vArray) // Only the first one will have been changed.
The answer I was looking for was to change the syntax in my answer to Array.push({...Object}). This creates a 'new' object to be pushed, with only 5 additional characters...
I didn't know 'object spread syntax' essentially did this.

Simplest way to copy JS object and filter out certain properties

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' }

Remove reference to another object in javascript

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.

Categories

Resources