Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you clone an array of objects in JavaScript?

Tags:

javascript

...where each object also has references to other objects within the same array?

When I first came up with this problem I just thought of something like

var clonedNodesArray = nodesArray.clone() 

would exist and searched for information on how to clone objects in JavaScript. I did find a question on Stack Overflow (answered by the very same @JohnResig) and he pointed out that with jQuery you could do

var clonedNodesArray = jQuery.extend({}, nodesArray); 

to clone an object. I tried this though, and this only copies the references of the objects in the array. So if I

nodesArray[0].value = "red" clonedNodesArray[0].value = "green" 

the value of both nodesArray[0] and clonedNodesArray[0] will turn out to be "green". Then I tried

var clonedNodesArray = jQuery.extend(true, {}, nodesArray); 

which deep copies an Object, but I got "too much recursion" and "control stack overflow" messages from both Firebug and Opera Dragonfly respectively.

How would you do it? Is this something that shouldn't even be done? Is there a reusable way of doing this in JavaScript?

like image 937
wallyqs Avatar asked Feb 28 '09 05:02

wallyqs


People also ask

How do you duplicate an array in JavaScript?

To duplicate an array, just return the element in your map call. numbers = [1, 2, 3]; numbersCopy = numbers. map((x) => x); If you'd like to be a bit more mathematical, (x) => x is called identity.


1 Answers

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 40% 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.*

like image 82
Vladimir Kharlampidi Avatar answered Oct 25 '22 05:10

Vladimir Kharlampidi