How to Clone an Array of Objects in JavaScript

How do you clone an array of objects in JavaScript?

Creating a deep copy with structuredClone

The modern way to deep copy an array in JavaScript is to use structuredClone:

array2 = structuredClone(array1);

However, this function is relatively new (Chrome 98, Firefox 94) and is currently only available to about 85% of users, so it's not ready for production yet without a polyfill.

As an alternative, you can use one of the well-supported JSON-based solutions below.

Creating a deep copy with JSON.parse

A general solution, that accounts for all possible objects inside an Array of objects may not be possible.
That said, if your array contains objects that have JSON-serializable content (no functions, no Number.POSITIVE_INFINITY, etc.) one simple way to avoid loops, at a performance cost, is this pure vanilla one-line solution.

let clonedArray = JSON.parse(JSON.stringify(nodesArray))

To summarize the comments below, the primary advantage of this approach is that it also clones the contents of the array, not just the array itself. The primary downsides are its limit of only working on JSON-serializable content, and it's performance is ~30 times slower than the spread method.

If you have shallow objects in the array, and IE6 is acceptable, a better approach is to use the spread operator combined with the .map array operator. For a two levels deep situation (like the array in the Appendix below):

clonedArray = nodesArray.map(a => {return {...a}})

The reasons are two fold: 1) It is much, much faster (see below for a benchmark comparison) and it will also allow any valid object in your array.

*Appendix:
The performance quantification is based on cloning this array of objects a million times:

 [{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic1.jpg?raw=true', id: '1', isFavorite: false}, {url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic2.jpg?raw=true', id: '2', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic3.jpg?raw=true', id: '3', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic4.jpg?raw=true', id: '4', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic5.jpg?raw=true', id: '5', isFavorite: true},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic6.jpg?raw=true', id: '6', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic7.jpg?raw=true', id: '7', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic8.jpg?raw=true', id: '8', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic9.jpg?raw=true', id: '9', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic10.jpg?raw=true', id: '10', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic11.jpg?raw=true', id: '11', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic12.jpg?raw=true', id: '12', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic13.jpg?raw=true', id: '13', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic14.jpg?raw=true', id: '14', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic15.jpg?raw=true', id: '15', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic16.jpg?raw=true', id: '16', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic17.jpg?raw=true', id: '17', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic18.jpg?raw=true', id: '18', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic19.jpg?raw=true', id: '19', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic20.jpg?raw=true', id: '20', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic21.jpg?raw=true', id: '21', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic22.jpg?raw=true', id: '22', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic23.jpg?raw=true', id: '23', isFavorite: false}]

either using:

let clonedArray = JSON.parse(JSON.stringify(nodesArray))

or:

clonedArray = nodesArray.map(a => {return {...a}})

The map/spread approach took 0.000466 ms per pass and the JSON.parse and JSON.stringify 0.014771 ms per pass.*

How to clone a Javascript Array of Objects?

EDIT

Deep clone use JSON.parse(JSON.stringify(arr));

Shallow clone Use slice(0);

var arr = [{'obj1':1}, {'obj2':2}];

var clone = arr.slice(0);

console.log(clone);

copy array of objects

The $.extend documentation says the following:

Undefined properties are not copied. However, properties inherited from the object's prototype will be copied over. Properties that are an object constructed via new MyCustomObject(args), or built-in JavaScript types such as Date or RegExp, are not re-constructed and will appear as plain Objects in the resulting object or array.

This means that the array with all plain object in it will be deeply merged/copied. However objects created with the new keyword will not be reconstructed. This leaves us with the following scenario:

The array copy works just fine, however since the elements in the array are created using the new keyword they are not further merged. When altering the array itself (pushing, popping, etc.) you can see that the array is indeed a copy.

The issue here is that you access one of the elements in the array and change the object (created with the new keyword). Both arrays still point to the same object, thus when reading from the other array which hold the same object reference you will also see this change.

demonstration image

To resolve this issue you have to also make a copy of each object in the array. Depending on your use-case you might be able to use Object.assign or Object.create have a look at the documentation before using them blindly.

I've also created a minimal example of the problem you face to give you some better understanding of the issue.

// setup

var array1, array2, array3, array4;

function Dummy(name) { this.name = name }

// test #1 - using plain objects

array1 = [{ name: 'Foo' }];

array2 = $.extend(true, [], array1);

array1[0].name = 'Bar';

console.log(array1[0].name, array2[0].name);

// test #2 - using the `new` keyword

array3 = [new Dummy('Foo')];

array4 = $.extend(true, [], array3);

array3[0].name = 'Bar';

console.log(array3[0].name, array4[0].name);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

There are at least 6 (!) ways to clone an array:

  • loop
  • slice
  • Array.from()
  • concat
  • spread operator (FASTEST)
  • map A.map(function(e){return e;});

There has been a huuuge BENCHMARKS thread, providing following information:

  • for blink browsers slice() is the fastest method, concat() is a bit slower, and while loop is 2.4x slower.

  • for other browsers while loop is the fastest method, since those browsers don't have internal optimizations for slice and concat.

This remains true in Jul 2016.

Below are simple scripts that you can copy-paste into your browser's console and run several times to see the picture. They output milliseconds, lower is better.

while loop

n = 1000*1000;
start = + new Date();
a = Array(n);
b = Array(n);
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

slice

n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);

Please note that these methods will clone the Array object itself, array contents however are copied by reference and are not deep cloned.

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true

What is the most efficient way to deep clone an object in JavaScript?

Native deep cloning

There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.

structuredClone(value)

If needed, loading the polyfill first:

import structuredClone from '@ungap/structured-clone';

See this answer for more details.

Older answers

Fast cloning with data loss - JSON.parse/stringify

If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:

JSON.parse(JSON.stringify(object))

const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()

JavaScript deep copy an array containing nested objects, arrays & functions?

Edit- You can use the solution below or just import Lodash and use this https://lodash.com/docs/#cloneDeep


I'm answering my own question with the solution I found. Someone posted this in the comment section of the article I linked and it seems to work

notes=[
{
contents: "Hello World 1",
function: console.log,
children: [
{
contents: "Hello World A",
function: console.log,
children: []
},
]
},
{
contents: "Hello World 2",
function: console.log,
children: []
}
]

function deepCopy(src) {
let target = Array.isArray(src) ? [] : {};
for (let key in src) {
let v = src[key];
if (v) {
if (typeof v === "object") {
target[key] = deepCopy(v);
} else {
target[key] = v;
}
} else {
target[key] = v;
}
}

return target;
}

Multiply/Clone multiple times objects inside and array in JavaScript

You could do it like this, where you simply fill an array with value elements, and map it to a clone of the original element:

const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
]

console.log(
data.flatMap((el) => new Array(el.value).fill(null).map(e => ({...el}))))


Related Topics



Leave a reply



Submit