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.*
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 Date
s, 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;
}
Deep copying an array of objects
You are almost there, where you have:
foodClone[i] = food[i].setItemName();
You probably want (in addition to the other variables of MenuItem)
foodClone[i].setItemName(food[i].getItemName())`
However, it's best to use the clone method or a copy constructor (well, copy constructor arguably might be best).
I do prefer using a copy constructor, such an example would be:
MenuItem(MenuItem menuItemToClone)
{
this.name = menuItemToClone.name;
this.descrip = menuItemToClone.descrip;
this.price = menuItemToClone.price;
}
Then you would just do:
foodClone[i] = new MenuItem(food[i]);
Deep copy of an object array
What you have implemented is a shallow copy. To implement a deep copy, you must
change
data[i] = other.data[i];
to some thing that assigns a copy of other.data[i]
to data[i]
. How you do this depends on the Position
class. Possible alternatives are:
a copy constructor:
data[i] = new Position(other.data[i]);
a factory method:
data[i] = createPosition(other.data[i]);
clone:
data[i] = (Position) other.data[i].clone();
Notes:
- The above assume that the copy constructor, factory method and clone method respectively implement the "right" kind of copying, depending on the Position class; see below.
- The
clone
approach will only work ifPosition
explicitly supports it, and this is generally regarded as an inferior solution. Besides, you need to be aware that the native implementation ofclone
(i.e. theObject.clone()
method) does a shallow copy1.
In fact the general problem of implementing deep copying in Java is complicated. In the case of the Position
class, one would assume that the attributes are all primitive types (e.g. ints or doubles), and therefore a deep versus shallow copying is moot. But if there are reference attributes, then you have to rely on the copy constructor / factory method / clone method to do the kind of copying that you require. In each case it needs to be programmed in. And in the general case (where you have to deal with cycles) it is difficult and requires each class to implement special methods.
There is one other potential way to copy an array of objects. If the objects in the array are serializable, then you can copy them by using ObjectOutputStream
and ObjectInputStream
serialize and then deserialize the array. However:
- this is expensive,
- it only works if the objects are (transitively) serializable, and
- the values of any
transient
fields won't be copied.
Copying by serialization is not recommended. It would be better to support cloning or some other method.
All in all, deep copying is best avoided in Java.
Finally, to answer your question about the Position
classes copy constructor works, I expect it is something like this:
public class Position {
private int x;
private int y;
...
public Position(Position other) {
this.x = other.x;
this.y = other.y;
}
...
}
As @Turtle says, there's no magic involved. You implement a constructor (by hand) that initializes its state by copying from an existing instance.
1 - It is specified that the Object implementation of clone()
does a shallow copy, but this may be overridden. The javadoc for clone
specifies the "contract" as follows:
"Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x, the expression:
x.clone() != x
will be true, and that the expression:x.clone().getClass() == x.getClass()
will be true, but these are not absolute requirements. While it is typically the case that:x.clone().equals(x)
will be true, this is not an absolute requirement."
Nothing in the "contract" talks about deep versus shallow copying. So if you are going to use clone
in this context, you need to know how the actual classes clone
method behaves.
javascript deep copy members from array of objects
let tempMax = slicedArr[0]
will just do the shallow copy instead you can do let tempMax = {...slicedArr[0]}
since your object is only at level one this will do a deepcopy,
if it is nested you can use loadash's cloneDeep to do a deepcopy.
Anywhere you are assiging a object to a variable it is a shallow copy
how to make a deep copy of a swift array of class objects
As a simple statement, you could use code like this:
var copiedArray = array.map{$0.copy()}
Note that the term "deepCopy" is a little misleading for what you're talking about. What if the array is heterogeneous and contains other containers like arrays, dictionaries, sets, and other custom container and "leaf" objects? What you should really do is to create a protocol DeepCopiable and make it a requirement that any object that conforms to DeepCopiable require that any child objects also conform to the DeepCopiable protocol, and write the deepCopy() method to recursively call deepCopy() on all child objects. That way you wind up with a deep copy that works at any arbitrary depth.
Related Topics
Is It Bad Practice to Make a Setter Return "This"
Factorial Using Recursion in Java
How to Convert Set<String> to String[]
Convert Localdate to Localdatetime or Java.Sql.Timestamp
How to Configure Httponly Cookies in Tomcat/Java Webapps
Why Does Intellij Give Me "Package Doesn't Exist" Error
Jackson/Hibernate, Meta Get Methods and Serialization
Java Parsing Xml Document Gives "Content Not Allowed in Prolog." Error
Cleanest Way to Toggle a Boolean Variable in Java
Add Multiple Items to an Already Initialized Arraylist in Java
What Is the Largest Possible Heap Size with a 64-Bit Jvm
Reading a Binary Input Stream into a Single Byte Array in Java
What Is the Shortest Way to Pretty Print a Org.W3C.Dom.Document to Stdout
Java 8 Lambda Expression and First-Class Values
Java Restfull Webservice: Jax-Rs Implementation with Jersey 2.3.1 Libraries