71

I have a scenario where i need to copy the array of Objects(Main array) to another Temp array which should not have object reference basically if i make any modification to Main array it should not reflect in the Temp array so that i will preserve the copy independently.

I have used one of the code snippet from stack overflow this one does partially like if i delete all objects from the Main array the temp array still hold the value but when i do some modifications in main array and click cancel button iam removing all objects from the main array using array.Removeall(); but the modification still exist in Temp array so which means that object having a reference.

clone: function (existingArray) {
  var newObj = (existingArray instanceof Array) ? [] : {};
  console.debug('newObj value is ' + newObj);
  for (i in existingArray) {
    console.debug('i value is' + i);
    if (i == 'clone') continue;
    console.debug('existingArray[i] value ' + existingArray[i]);
    if (existingArray[i] && typeof existingArray[i] == "object") {

      newObj[i] = this.clone(existingArray[i]);
    } else {
      console.debug('in else part ' + existingArray[i]);
      newObj[i] = existingArray[i];
    }
  }
  return newObj;
}

my object structure is like

iam using knockout framework.

newObjectCreation = function (localIp, RemoteIp, areaId) {
  this.localIP = ko.observable(localIp);
  this.remoteIP = ko.observable(RemoteIp);
  this.areaId = ko.observable(areaId);
};

template.ProtocolArray.push(new newObjectCreation('', '', '')); // to create default row
starball
  • 20,030
  • 7
  • 43
  • 238
sriramdev
  • 759
  • 1
  • 5
  • 11
  • 1
    Do the objects contain anything which is not expressible as [JSON](http://json.org/) (not object literal)? If not, you could simple do: `var clone = JSON.parse(JSON.stringify(src));` – Yoshi Mar 27 '12 at 08:38
  • Related: http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object – AlikElzin-kilaka Dec 29 '15 at 08:12
  • Possible duplicate of [Copying array by value in JavaScript](http://stackoverflow.com/questions/7486085/copying-array-by-value-in-javascript) – kchetan Feb 19 '16 at 08:52

9 Answers9

135

Let me understand: you don't want just have a new array, but you want to create a new instance for all objects are present in the array itself? So if you modify one of the objects in the temp array, that changes is not propagated to the main array?

If it's the case, it depends by the values you're keeping in the main array. If these objects are simple objects, and they can be serialized in JSON, then the quickest way is:

var tempArray = JSON.parse(JSON.stringify(mainArray));

If you have more complex objects (like instances created by some your own constructors, html nodes, etc) then you need an approach ad hoc.

Edit:

If you don't have any methods on your newObjectCreation, you could use JSON, however the constructor won't be the same. Otherwise you have to do the copy manually:

var tempArray = [];
for (var i = 0, item; item = mainArray[i++];) {
    tempArray[i] = new newObjectCreation(item.localIP, item.remoteIP, item.areaId);
}
ZER0
  • 24,846
  • 5
  • 51
  • 54
  • so will var tempArray = JSON.parse(JSON.stringify(mainArray)); do a deep copy i.e without object reference? 2.)regarding that manual copying doesnt this manual copying still point to same object reference. – sriramdev Mar 27 '12 at 09:06
  • if you have only properties and not methods, and you don't care about having exactly the same object with the same constructor, but just have plain object that have the same values, yes, you will have a sort of "deep copy". To make it clears: `function foo(a) { this.a = a }; var main = [new foo(1)], temp = JSON.parse(JSON.stringify(main));` then you will have `main[0].a` equals to `1` and `temp[0].a` as well. If you change the last one `temp[0].a = 4` that changes won't be reflected to `main[0].a`. However `main[0].constructor` is `foo` and `temp[0].constructor` is `Object`. – ZER0 Mar 27 '12 at 09:11
  • Thanks a lot for that answer I appreciate your help template.Temparray(JSON.parse(JSON.stringify(ko.toJS(template.Mainarray())))); I have used above snippet and it works as expected with n=knockout framework.Kudos to u :) – sriramdev Mar 27 '12 at 10:46
  • 1
    Why doesn't JavaScript have a function to do this? Like `function cloneObject (o) { return JSON.parse(JSON.stringify(o)); }` ...except doing something faster behind-the-scenes. Or even better: a method for all objects called `clone()` so you could just do `mainArray.clone()`. – Luke Mar 07 '14 at 21:13
  • 1
    Thank you @ZER0! Using the native JSON functions is the most elegant suggestion to do this that I have seen. – ryanttb Nov 26 '14 at 23:11
36

For some other people with the same question. You could also do it this way.
Using the new es6 features you could create a copy of an array (without reference) and a copy of every object without one level of references.

const copy = array.map(object => ({ ...object }))

It's much more functional and idiomatic IMHO

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

So basically if your objects doesn't have objects as properties. This syntax is everything you need. Unfortunately there is not "out of the box" deep clone feature on the spec but you can always use a library if that's what you need

Browser Compatibility Warning: I think it is part of the specification of Ecma now, but some browsers doesn't have full support of spread syntax jet. But using one of the popular transpilers out there you will be fine

Iván E. Sánchez
  • 1,183
  • 15
  • 28
22

Lodash can be used for deep copying objects _.cloneDeep(value)

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

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// → false
tomtomssi
  • 1,017
  • 5
  • 20
  • 33
18

To copy the values of an array without copying the reference of the array, you can simply do:

const tempArray = [...mainArray];

This is the recommended solution for AirBnb's JS Style Guide: https://github.com/airbnb/javascript#arrays

However, this will not create new referenes for the objects inside the array. To create a new reference for the array and the objects inside, you can do:

JSON.parse(JSON.stringify(mainArray));
Martin Brandhaug
  • 1,052
  • 1
  • 10
  • 21
  • 20
    Arrays are without reference but the object inside is. – eomeroff Apr 05 '18 at 16:08
  • 2
    For an array of arrays, try this: `var start = [[4,2,6,5],[1,8,7,3]]; var temp = start.map(e => e.slice(0)); var tempEs6 = start.map(e => [...e]);` – Katie Jul 13 '19 at 04:46
10

So you want a deep copy without object reference? Sure, use .slice().

Example:

var mainArr = [],
    tmpArr = []

tmpArr = mainArr.slice(0) // Shallow copy, no reference used.

PS: I don't think double-JSON parsing is performance wise.

Florian Margaine
  • 58,730
  • 15
  • 91
  • 116
  • 21
    This did not work for me under Google Chrome 25. After I modified the new array's first object it also modified the original array's first object. No downvote though as it does work with simple arrays, just not with arrays of objects which I think is the OP's point. – Purefan Mar 16 '13 at 13:06
  • This didnt work for me either. http://stackoverflow.com/questions/28482593/copying-an-array-of-objects-into-another-array-in-javascript-deep-copy – jsbisht Feb 12 '15 at 16:26
  • 1
    But double-JSON parsing works... According to MDN (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/slice): "For object references (and not the actual object), slice copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays." – El Kopyto Feb 01 '16 at 06:23
  • 3
    This won't work if the array has objects in it. It will copy the object references. – Scotty Jun 08 '16 at 10:25
  • 1
    As everyone said. This simply does not work unless you have simple objects in the array. This won't work when using most Js frameworks in most cases (this is an oversimplification). This answer does not answer the question. – dewwwald May 01 '17 at 13:28
  • This won't work if you have object type {} in your array – Adamy Oct 16 '18 at 22:00
  • This also doesn't work if you have an **array of arrays** like `[[4,2,6,5],[1,8,7,3]]`. In this case, you can do the following: `var start = [[4,2,6,5],[1,8,7,3]]; var temp = start.map(e => e.slice(0))` – Katie Jul 13 '19 at 04:37
3

You can use Angular's copy: angular.copy();

AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
1

On nested array you can do:

    const origin = [{ cat: 'Bengal', dog: 'Birman' }, { cat: 'Abyssinian', dog: 'Bombay' }];
    const clone = [];
    origin.forEach(v=> clone.push(Object.assign({}, v)));
dawid debinski
  • 392
  • 4
  • 6
1

Use this function https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

let deepClonedArrayOfObjects = structuredClone(originalArrayOfObjects);
harsh trivedi
  • 11
  • 1
  • 2
0

Use angular.copy. But not for the whole array (because it would pass array items by reference), but iterate it and use angular.copy on its members.

var newArray = [];
for (var i = 0, item; item = mainArray[i];) {
    newArray[i] = angular.copy(item);
    i++;
}
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63