[arrays] Cloning an array in Javascript/Typescript

I have array of two objects:

genericItems: Item[] = [];
backupData: Item[] = [];

I am populating my HTML table with genericItemsdata. The table is modifiable. There is a reset button to undo all changes done with backUpData. This array is populated by a service:

getGenericItems(selected: Item) {
this.itemService.getGenericItems(selected).subscribe(
  result => {
     this.genericItems = result;
  });
     this.backupData = this.genericItems.slice();
  }

My idea was that, the user changes will get reflected in first array and second array can be used as backup for reset operation. The issue I am facing here is when the user modifies the table (genericItems[]) the second array backupData also gets modified.

How is this happening and how to prevent this?

This question is related to arrays angular typescript

The answer is


Below code might help you to copy the first level objects

let original = [{ a: 1 }, {b:1}]
const copy = [ ...original ].map(item=>({...item}))

so for below case, values remains intact

copy[0].a = 23
console.log(original[0].a) //logs 1 -- value didn't change voila :)

Fails for this case

let original = [{ a: {b:2} }, {b:1}]
const copy = [ ...original ].map(item=>({...item}))
copy[0].a.b = 23;
console.log(original[0].a) //logs 23 -- lost the original one :(

Final advice:

I would say go for lodash cloneDeep API which helps you to copy the objects inside objects completely dereferencing from original one's. This can be installed as a separate module.

Refer documentation: https://github.com/lodash/lodash

Individual Package : https://www.npmjs.com/package/lodash.clonedeep


try the following code:

this.cloneArray= [...this.OriginalArray]

The following line in your code creates a new array, copies all object references from genericItems into that new array, and assigns it to backupData:

this.backupData = this.genericItems.slice();

So while backupData and genericItems are different arrays, they contain the same exact object references.

You could bring in a library to do deep copying for you (as @LatinWarrior mentioned).

But if Item is not too complex, maybe you can add a clone method to it to deep clone the object yourself:

class Item {
  somePrimitiveType: string;
  someRefType: any = { someProperty: 0 };

  clone(): Item {
    let clone = new Item();

    // Assignment will copy primitive types

    clone.somePrimitiveType = this.somePrimitiveType;

    // Explicitly deep copy the reference types

    clone.someRefType = {
      someProperty: this.someRefType.someProperty
    };

    return clone;
  }
}

Then call clone() on each item:

this.backupData = this.genericItems.map(item => item.clone());

Try this:

[https://lodash.com/docs/4.17.4#clone][1]

var objects = [{ 'a': 1 }, { 'b': 2 }];

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

Try this

const returnedTarget = Object.assign(target, source);

and pass empty array to target

in case complex objects this way works for me

$.extend(true, [], originalArray) in case of array

$.extend(true, {}, originalObject) in case of object


Using map or other similar solution do not help to clone deeply an array of object. An easier way to do this without adding a new library is using JSON.stringfy and then JSON.parse.

In your case this should work :

this.backupData = JSON.parse(JSON.stringify(genericItems));

It looks like you may have made a mistake as to where you are doing the copy of an Array. Have a look at my explanation below and a slight modification to the code which should work in helping you reset the data to its previous state.

In your example i can see the following taking place:

  • you are doing a request to get generic items
  • after you get the data you set the results to the this.genericItems
  • directly after that you set the backupData as the result

Am i right in thinking you don't want the 3rd point to happen in that order?

Would this be better:

  • you do the data request
  • make a backup copy of what is current in this.genericItems
  • then set genericItems as the result of your request

Try this:

getGenericItems(selected: Item) {
  this.itemService.getGenericItems(selected).subscribe(
    result => {
       // make a backup before you change the genericItems
       this.backupData = this.genericItems.slice();

       // now update genericItems with the results from your request
       this.genericItems = result;
    });
  }

If your items in the array are not primitive you can use spread operator to do that.

this.plansCopy = this.plans.map(obj => ({...obj}));

Complete answer : https://stackoverflow.com/a/47776875/5775048


I have the same issue with primeNg DataTable. After trying and crying, I've fixed the issue by using this code.

private deepArrayCopy(arr: SelectItem[]): SelectItem[] {
    const result: SelectItem[] = [];
    if (!arr) {
      return result;
    }
    const arrayLength = arr.length;
    for (let i = 0; i <= arrayLength; i++) {
      const item = arr[i];
      if (item) {
        result.push({ label: item.label, value: item.value });
      }
    }
    return result;
  }

For initializing backup value

backupData = this.deepArrayCopy(genericItems);

For resetting changes

genericItems = this.deepArrayCopy(backupData);

The magic bullet is to recreate items by using {} instead of calling constructor. I've tried new SelectItem(item.label, item.value) which doesn't work.


Looks like what you want is Deep Copy of the object. Why not use Object.assign()? No libaries needed, and its a one-liner :)

getGenericItems(selected: Item) {
    this.itemService.getGenericItems(selected).subscribe(
        result => {
            this.genericItems = result;
            this.backupDate = Object.assign({}, result); 
            //this.backupdate WILL NOT share the same memory locations as this.genericItems
            //modifying this.genericItems WILL NOT modify this.backupdate
        });
}

More on Object.assign(): https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


Cloning Arrays and Objects in javascript have a different syntax. Sooner or later everyone learns the difference the hard way and end up here.

In Typescript and ES6 you can use the spread operator for array and object:

const myClonedArray  = [...myArray];  // This is ok for [1,2,'test','bla']
                                      // But wont work for [{a:1}, {b:2}]. 
                                      // A bug will occur when you 
                                      // modify the clone and you expect the 
                                      // original not to be modified.
                                      // The solution is to do a deep copy
                                      // when you are cloning an array of objects.

To do a deep copy of an object you need an external library:

import {cloneDeep} from 'lodash';
const myClonedArray = cloneDeep(myArray);     // This works for [{a:1}, {b:2}]

The spread operator works on object as well but it will only do a shallow copy (first layer of children)

const myShallowClonedObject = {...myObject};   // Will do a shallow copy
                                               // and cause you an un expected bug.

To do a deep copy of an object you need an external library:

 import {cloneDeep} from 'lodash';
 const deeplyClonedObject = cloneDeep(myObject);   // This works for [{a:{b:2}}]

you can use map function

 toArray= fromArray.map(x => x);

the easiest way to clone an array is

backUpData = genericItems.concat();

This will create a new memory for the array indexes


Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?