94

I have an array of objects that is an input. Lets call it content.

When trying to deep copy it, it still has a reference to the previous array.

I need to duplicate that input array, and change one property of the duplicated part.

So long I've tried different methods that weren't successful.

ES6 way:

public duplicateArray() {
  arr = [...this.content]
  arr.map((x) => {x.status = DEFAULT});
  return this.content.concat(arr);
}

The slice way:

public duplicateArray() {
  arr = this.content.slice(0);
  arr.map((x) => {x.status = DEFAULT});
  return this.content.concat(arr);
}

In both of them all the objects inside the array have status: 'Default'.

What's the best approach to deep copy the array in Angular 2?

11 Answers 11

146

Check this:

  let cloned = source.map(x => Object.assign({}, x));
Sign up to request clarification or add additional context in comments.

6 Comments

if i'm not missing something this doesn't work for strings-- when I try var source = ["one","two","three"]; var cloned = source.map(x => Object.assign({},x)); I end up with cloned as: [ { '0': 'o', '1': 'n', '2': 'e' }, { '0': 't', '1': 'w', '2': 'o' }, { '0': 't', '1': 'h', '2': 'r', '3': 'e', '4': 'e' } ]
As for me source.map(x => ({ ...x })); suits the best.
If you want to clone the array object, This option is more preferable.
After 2 hours of search. This worked! THANKS A TON!
|
59

Simple:

let objCopy  = JSON.parse(JSON.stringify(obj));

This Also Works (Only for Arrays)

let objCopy2 = obj.slice()

7 Comments

may not be most efficient but definitely the easiest... and will work for any objects / arrays
This may be a good idea. But there is a but: I do not think this copies methods, assuming the objects have methods.
Saved my day than Object.assign([], this.list); (line)
If the object has function properties or cyclic references this won't work.
JSON.stringify has a replace function JSON.stringify(<object>, <replacer func>) for handing cyclical and function properties
|
18

This is Daria's suggestion (see comment on the question) which works starting from TypeScript 2.1 and basically clones each element from the array:

this.clonedArray = theArray.map(e => ({ ... e }));

4 Comments

This worked for me! Thank you Alexei!! Very simple and elegant :)
Seems very quick too and no need to include additional lodash libraries.
I find this the only way that actually deep clones. JSON.parse(JSON.stringify(obj)) works but converts integer values into strings.
@Abhi I have also relied on this for some cases such as when working with a rather big state in a reducer which must be cloned to ensure function purity.
16

This is working for me:

this.listCopy = Object.assign([], this.list);

9 Comments

@makkasi That's not true. changing this.list after this.listCopy has been set, will not affect this.listCopy for sure!!
OK.May be I'm wrong. I tested with this code and it changes the other list. May be the reason is somewhere else. I don't have access to computer at the moment. Will try this later. I deleted my previous comment.
@kabus, it will if this.list contains objects. If you modify any object contained in this.list, the changes will be reflected to this.listCopy, because it keeps just references.
@el.atomo can you provide an example, because i cant reproduce it
Sure @kabus, let list = [{a: 1}]; let listCopy = Object.assign([], list); list[0].a = 2; console.log(list[0].a, listCopy[0].a);. Sorry for the ugly formatting :)
|
13

The only solution I've found (almost instantly after posting the question), is to loop through the array and use Object.assign()

Like this:

public duplicateArray() {
  let arr = [];
  this.content.forEach((x) => {
    arr.push(Object.assign({}, x));
  })
  arr.map((x) => {x.status = DEFAULT});
  return this.content.concat(arr);
}

I know this is not optimal. And I wonder if there's any better solutions.

1 Comment

10

A clean way of deep copying objects having nested objects inside is by using lodash's cloneDeep method.

For Angular, you can do it like this:

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.

1 Comment

Be aware, in case your originalObject is an array of objects, objects won't be deep copied, their reference will be copied.
9

I found deep copy method in angular devkit, It's very normal, so... maybe you can just implement yourself or use that.

I prefer to use loadash, there a lot of objects and array operation methods that can use.

import { deepCopy } from '@angular-devkit/core/src/utils/object';

export class AppComponent {
  source = {
    ....
  }
  constructor() {
     const newObject = deepCopy(this.source);
  }
}
Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.1000.8
@angular-devkit/build-angular     0.1000.8
@angular-devkit/build-optimizer   0.1000.8
@angular-devkit/build-webpack     0.1000.8
@angular-devkit/core              10.0.8
@angular-devkit/schematics        10.0.8
@angular/cli                      10.0.8
@ngtools/webpack                  10.0.8
@schematics/angular               10.0.8
@schematics/update                0.1000.8
rxjs                              6.5.5
typescript                        3.9.7
webpack                           4.43.0

1 Comment

Awsome solution, thumbs up
1

Here is my own. Doesn't work for complex cases, but for a simple array of Objects, it's good enough.

  deepClone(oldArray: Object[]) {
    let newArray: any = [];
    oldArray.forEach((item) => {
      newArray.push(Object.assign({}, item));
    });
    return newArray;
  }

Comments

1

Alternatively, you can use the GitHub project ts-deepcopy, which is also available on npm, to clone your object, or just include the code snippet below.

/**
 * Deep copy function for TypeScript.
 * @param T Generic type of target/copied value.
 * @param target Target value to be copied.
 * @see Source project, ts-deepcopy https://github.com/ykdr2017/ts-deepcopy
 * @see Code pen https://codepen.io/erikvullings/pen/ejyBYg
 */
export const deepCopy = <T>(target: T): T => {
  if (target === null) {
    return target;
  }
  if (target instanceof Date) {
    return new Date(target.getTime()) as any;
  }
  if (target instanceof Array) {
    const cp = [] as any[];
    (target as any[]).forEach((v) => { cp.push(v); });
    return cp.map((n: any) => deepCopy<any>(n)) as any;
  }
  if (typeof target === 'object' && target !== {}) {
    const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any };
    Object.keys(cp).forEach(k => {
      cp[k] = deepCopy<any>(cp[k]);
    });
    return cp as T;
  }
  return target;
};

1 Comment

Be aware that it does not clone empy objects which may cause unwanted behavior (you can remove the 'target !== {}' to fix this)
0

In case you need to copy an list of Objects

const newArray = originalArray.map(e => structuredClone(e));

If needed, loading the polyfill first:

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

See this answer for more details.

Comments

-3

you can use use JQuery for deep copying :

var arr =[['abc'],['xyz']];
var newArr = $.extend(true, [], arr);
newArr.shift().shift();

console.log(arr); //arr still has [['abc'],['xyz']]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.