Deep copying objects in Angular

Angular

Angular Problem Overview


AngularJS has angular.copy() to deep copy objects and arrays.

Does Angular also have something like that?

Angular Solutions


Solution 1 - Angular

You can also use:

JSON.parse(JSON.stringify(Object))

if it's on your scope, it's in every Angular component, directive, etc. and it's also on every node environment.

Unless you have a circular reference, it should work and will effectively dissociate your variable reference to the original object.

Solution 2 - Angular

This question isn't a duplicate of https://stackoverflow.com/questions/34688517/how-can-i-use-angular-copy-in-angular-2 because the OP is asking about deep copying objects. The linked answer recommends Object.assign() which doesn't make a deep copy.

Actually, using Angular2 doesn't restrict you from using other libraries like jQuery for deep copying objects with their $.clone() function or lodash with _.cloneDeep().

The most common libraries have their typings available via typings CLI tools so even when transpiling from TypeScript you can seamlessly use anything you want.

Also see: https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript

Solution 3 - Angular

Another option is to implement your own function:

/**
 * Returns a deep copy of the object
 */
public static deepCopy(oldObj: any) {
    var newObj = oldObj;
    if (oldObj && typeof oldObj === "object") {
        if (oldObj instanceof Date) {
           return new Date(oldObj.getTime());
        }
        newObj = Object.prototype.toString.call(oldObj) === "[object Array]" ? [] : {};
        for (var i in oldObj) {
            newObj[i] = this.deepCopy(oldObj[i]);
        }
    }
    return newObj;
}

Solution 4 - Angular

You can deep copy an object in Angular by using lodash's cloneDeep method:

Install lodash with yarn add lodash or npm install lodash.

In your component, import cloneDeep and use it:

import * as cloneDeep from 'lodash/cloneDeep';
...
clonedObject = cloneDeep(originalObject);

It's only 18kb added to your build, well worth for the benefits.

I've also written an article here, if you need more insight on why using lodash's cloneDeep.

Solution 5 - Angular

Create helper class with name deepCopy.ts

/*
* DeepCopy class helps to copy an Original Array or an Object without impacting on original data
*/

export class DeepCopy {

  static copy(data: any) {
    let node;
    if (Array.isArray(data)) {
      node = data.length > 0 ? data.slice(0) : [];
      node.forEach((e, i) => {
        if (
          (typeof e === 'object' && e !== {}) ||
          (Array.isArray(e) && e.length > 0)
        ) {
          node[i] = DeepCopy.copy(e);
        }
      });
    } else if (data && typeof data === 'object') {
      node = data instanceof Date ? data : Object.assign({}, data);
      Object.keys(node).forEach((key) => {
        if (
          (typeof node[key] === 'object' && node[key] !== {}) ||
          (Array.isArray(node[key]) && node[key].length > 0)
        ) {
          node[key] = DeepCopy.copy(node[key]);
        }
      });
    } else {
      node = data;
    }
    return node;
  }
}

Import deepCopy file where ever you required and use as below code DeepCopy.copy(arg); , Here arg would either object or array which you want

Solution 6 - Angular

Some modifications for KrishnamrajuK's answer

export class DeepCopy {
  static copy(data: any, objMap?: WeakMap<any, any>) {
    if (!objMap) {
      // Map for handle recursive objects
      objMap = new WeakMap();
    }

    // recursion wrapper
    const deeper = value => {
      if (value && typeof value === 'object') {
        return DeepCopy.copy(value, objMap);
      }
      return value;
    };

    // Array value
    if (Array.isArray(data)) return data.map(deeper);

    // Object value
    if (data && typeof data === 'object') {
      // Same object seen earlier
      if (objMap.has(data)) return objMap.get(data);
      // Date object
      if (data instanceof Date) {
        const result = new Date(data.valueOf());
        objMap.set(data, result);
        return result;
      }
      // Use original prototype
      const node = Object.create(Object.getPrototypeOf(data));
      // Save object to map before recursion
      objMap.set(data, node);
      for (const [key, value] of Object.entries(data)) {
        node[key] = deeper(value);
      }
      return node;
    }
    // Scalar value
    return data;
  }
}

Solution 7 - Angular

If the source is an array of objects, using map:

let cloned = source.map(x => Object.assign({}, x));

OR

let cloned = source.map((x) => {
                return { ...x };
             });

Solution 8 - Angular

I have created a very simple function in typescript which accepts all possible inputs and gives the deep cloned copy of that object.

Hope it will help somebody.

  public deepCopy(obj) {

    var clonedObject: any;

    if (obj instanceof Array) {
        var itemArray = Object.assign([], obj);
        clonedObject = itemArray;

        for (var j = 0; j < clonedObject.length; j++) {
            clonedObject[j] = this.deepCopy(clonedObject[j]);
        }

        return clonedObject;
    }
    else if (typeof obj === 'number' || typeof obj == 'string') {
        return obj
    }
    else {
        

        var item = Object.assign({}, obj);
        clonedObject = item;

        let allKeys = Object.keys(clonedObject);

        for (var i = 0; i < allKeys.length; i++) {
            if (clonedObject[allKeys[i]] instanceof Array) {
                //If the calue is Array
                clonedObject[allKeys[i]] = this.deepCopy(clonedObject[allKeys[i]]);
            }
            else if (clonedObject[allKeys[i]] instanceof Date) {
                clonedObject[allKeys[i]] = new Date(clonedObject[allKeys[i]].valueOf());
            }
            else if (clonedObject[allKeys[i]] instanceof Object){
                //if the value is JOBJECT.
                clonedObject[allKeys[i]] = this.deepCopy(clonedObject[allKeys[i]]);
            }
        }
        return clonedObject;
    }


}

Solution 9 - Angular

I am faced with the problem of deep copying. angular.copy({}, factory) and angular.extend({}, factory) helps well for array or hashes objects, but when copying an object a calass, sometimes there may be problems with connected dependencies. I solved this problem so:

 copyFactory = (() ->
    resource = ->
      resource.__super__.constructor.apply this, arguments
      return
    this.extendTo resource
    resource
  ).call(factory)

Solution 10 - Angular

You can try this: https://www.npmjs.com/package/ngx-scv-util

I faced the same issue and created an npm package for the same. I know its an overhead just for this functionality :) . I plan to add more to this package over time. This one has been tested on 10.1.6 version of Angular. It should definitely work with versions higher than 10.1.6. Usage details are mentioned on the npm package page.

Solution 11 - Angular

Some of the answers here rely on lodash which can cause issues when imported in angular 10+ with a warning message like the following:

WARNING in xxxxx.ts depends on lodash/cloneDeep. CommonJS or AMD dependencies can cause optimization bailouts.

Other answers use parsing via JSON or try to roll their own implementation. Not to roll ours, we use clone: a lightweight clone utility with limited dependencies.

In order to use clone, you just need to install these two packages:

npm install clone
npm install --save-dev @types/clone

The create a service (the service is not mandatory, but I prefer this approach) that uses the clone API:

import { Injectable } from '@angular/core';
import * as clone from 'clone';

@Injectable()
export class ObjectCloneService {
    public cloneObject<T>(value: T): T {
        return clone<T>(value);
    }
}

Remember to add the service to your module.

Solution 12 - Angular

To use cloneDeep without any "CommonJS or AMD dependencies can cause optimization bailouts.", you can use it like this:

npm i lodash-es --save

Then simply in any component:

import { cloneDeep } from 'lodash-es';
// ...
let a = cloneDeep(b);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnkit SinghView Question on Stackoverflow
Solution 1 - AngularGabriel Balsa CantúView Answer on Stackoverflow
Solution 2 - AngularmartinView Answer on Stackoverflow
Solution 3 - AngularAndrea IalentiView Answer on Stackoverflow
Solution 4 - AngularBogdanCView Answer on Stackoverflow
Solution 5 - AngularKrishnamraju KView Answer on Stackoverflow
Solution 6 - Angularvp_arthView Answer on Stackoverflow
Solution 7 - AngularLahar ShahView Answer on Stackoverflow
Solution 8 - AngularAnil KumarView Answer on Stackoverflow
Solution 9 - AngularViktor IvliievView Answer on Stackoverflow
Solution 10 - AngularSharathView Answer on Stackoverflow
Solution 11 - AngularYenneferView Answer on Stackoverflow
Solution 12 - AngularSmokovskyView Answer on Stackoverflow