Copier le tableau par valeur

1745

Lors de la copie d'un tableau en JavaScript vers un autre tableau:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

J'ai réalisé que cela arr2fait référence au même tableau que arr1, plutôt qu'à un nouveau tableau indépendant. Comment puis-je copier le tableau pour obtenir deux tableaux indépendants?

Dan
la source
3
Il semble que actuellement dans Chrome 53 et Firefox 48, nous avons des performances sliceet des spliceopérations intéressantes et un nouvel opérateur de propagation et Array.fromune implémentation beaucoup plus lente. Regardez perfjs.fnfo
Pencroff
jsben.ch/#/wQ9RU <= ce benchmark donne un aperçu des différentes façons de copier un tableau
EscapeNetscape
Pour ce tableau (qui contient des chaînes primitives) , vous pouvez utiliser var arr2 = arr1.splice();pour copie en profondeur, mais cette technique ne fonctionnera pas si les éléments de votre tableau contient des structures littérales (c. -à- []ou {}) ou des objets prototypes (c. -à- function () {}, new, etc.). Voir ma réponse ci-dessous pour d'autres solutions.
tfmontague
16
Nous sommes en 2017, vous pouvez donc envisager d'utiliser les fonctionnalités d'ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich

Réponses:

2701

Utilisez ceci:

var newArray = oldArray.slice();

Fondamentalement, l' slice()opération clone le tableau et renvoie une référence à un nouveau tableau.

Notez également que:

Pour les références, les chaînes et les nombres (et non l'objet réel), slice()copie les références d'objet dans le nouveau tableau. Le tableau d'origine et le nouveau se réfèrent au même objet. Si un objet référencé change, les modifications sont visibles à la fois dans les nouveaux tableaux et dans les tableaux d'origine.

Les primitives telles que les chaînes et les nombres sont immuables, donc les modifications de la chaîne ou du nombre sont impossibles.

Saket
la source
9
En ce qui concerne les performances, les tests jsPerf suivants montrent réellement que var arr2 = arr1.slice () est tout aussi rapide que var arr2 = arr1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 et jsperf.com/copy-simple-array . Le résultat de jsperf.com/array-copy/5 m'a un peu surpris au point que je me demande si le code de test est valide.
Cohen
94
Même si cela a déjà reçu une tonne de votes positifs, il en mérite un autre car il décrit correctement les références dans JS, ce qui est malheureusement assez rare.
Wayne
34
@ GáborImre vous ajouteriez une bibliothèque entière simplement pour la lisibilité? Vraiment? Je voudrais juste ajouter un commentaire si j'étais préoccupé par la lisibilité. Voir: var newArray = oldArray.slice (); // Clone oldArray vers newArray
dudewad
12
@ GáborImre Je comprends cela, bien sûr. Mais répondre à un problème d'ingénierie spécifique en incluant une bibliothèque entière à mon avis n'est pas utile, c'est un gonflement de conception. Je vois que les développeurs font beaucoup cela, puis vous vous retrouvez avec un projet qui comprenait un framework complet pour remplacer le fait d'avoir à écrire une seule fonction. Mais juste mon MO.
dudewad
5
Leçon apprise: ne confondez pas .slice()avec .splice(), ce qui vous donne un tableau vide. Grande différence.
crazypeter
531

En Javascript, les techniques de copie profonde dépendent des éléments d'un tableau. Commençons par là.

Trois types d'éléments

Les éléments peuvent être: des valeurs littérales, des structures littérales ou des prototypes.

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}`

À partir de ces éléments, nous pouvons créer trois types de tableaux.

// 1) Array of literal-values (boolean, number, string) 
const type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
const type2 = [[], {}];

// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];

Les techniques de copie profonde dépendent des trois types de tableaux

En fonction des types d'éléments dans le tableau, nous pouvons utiliser diverses techniques pour copier en profondeur.

Techniques de copie en profondeur Javascript par type d'élément

  • Tableau de valeurs littérales (type1)
    Le [...myArray], myArray.splice(0), myArray.slice()et les myArray.concat()techniques peuvent être utilisées pour de profondes tableaux de copie avec des valeurs littérales (booléen, nombre et cordes) seulement; où l'opérateur Spread [...myArray]a les meilleures performances ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).

  • Tableau de valeurs littérales (type1) et structures littérales (type2)
    La JSON.parse(JSON.stringify(myArray))technique peut être utilisée pour copier en profondeur des valeurs littérales (booléen, nombre, chaîne) et des structures littérales (tableau, objet), mais pas des objets prototypes.

  • Tous les tableaux (type1, type2, type3)
    La $.extend(myArray)technique jQuery peut être utilisée pour copier en profondeur tous les types de tableaux. Des bibliothèques comme Underscore et Lo-dash offrent des fonctions de copie en profondeur similaires à jQuery $.extend() , mais ont des performances inférieures. Plus surprenant, $.extend()ses performances sont supérieures à celles de la JSON.parse(JSON.stringify(myArray))technique http://jsperf.com/js-deep-copy/15 .
    Et pour les développeurs qui évitent les bibliothèques tierces (comme jQuery), vous pouvez utiliser la fonction personnalisée suivante; qui a des performances supérieures à $ .extend et copie en profondeur tous les tableaux.

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

Donc pour répondre à la question ...

Question

var arr1 = ['a','b','c'];
var arr2 = arr1;

J'ai réalisé que arr2 fait référence au même tableau que arr1, plutôt qu'à un nouveau tableau indépendant. Comment puis-je copier le tableau pour obtenir deux tableaux indépendants?

Réponse

Étant donné qu'il arr1s'agit d'un tableau de valeurs littérales (booléen, nombre ou chaîne), vous pouvez utiliser n'importe quelle technique de copie approfondie décrite ci-dessus, où l'opérateur d'étalement ...a les performances les plus élevées.

// Highest performance for deep copying literal values
arr2 = [...arr1];

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above
tfmontague
la source
1
Beaucoup de ces approches ne fonctionnent pas bien. L'utilisation de l'opérateur d'affectation signifie que vous devez réaffecter la valeur littérale d'origine de arr1. Il est très rare que ce soit le cas. Utiliser des spliceoblitérés arr1, donc ce n'est pas du tout une copie. L'utilisation JSONéchouera si l'une des valeurs du tableau est des fonctions ou a des prototypes (comme a Date).
Dancrumb le
L'utilisation de l'épissure est une solution partielle. Il échouera dans bien plus de cas que JSON. Splice crée une copie complète des chaînes et des nombres, quand il déplace des valeurs - jamais dit qu'il retourne une copie.
tfmontague
1
Pourquoi épisser (0)? Cela ne devrait-il pas être slice ()? Je pense qu'il est censé ne pas modifier le tableau d'origine, ce que fait l'épissure. @JamesMontagne
helpse
2
splice créera des pointeurs vers les éléments du tableau d'origine (copie superficielle). splice (0) allouera une nouvelle mémoire (copie complète) aux éléments du tableau qui sont des nombres ou des chaînes et créera des pointeurs pour tous les autres types d'éléments (copie superficielle). En passant une valeur de départ de zéro à la méthode de fonction d'épissure, il n'épissera aucun élément du tableau d'origine et ne le modifie donc pas.
tfmontague
1
En fait, il n'y a qu'un seul type de tableau: un tableau de "quelque chose". Il n'y a aucune différence entre [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]et tout autre tableau.
wizzwizz4
189

Vous pouvez utiliser des spreads ...de tableaux pour copier des tableaux.

const itemsCopy = [...items];

De plus, si vous souhaitez créer un nouveau tableau avec celui existant en faisant partie:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Les spreads de tableaux sont désormais pris en charge dans tous les principaux navigateurs, mais si vous avez besoin d'un support plus ancien, utilisez tapuscript ou babel et compilez vers ES5.

Plus d'informations sur les spreads

Luke Femur
la source
1
Cela ne fonctionnera pas pour une copie complète. Deep Clone an Array en JavaScript .
SNag
151

Aucune jQuery nécessaire ... Exemple de travail

var arr2 = arr1.slice()

Cela copie le tableau de la position de départ 0jusqu'à la fin du tableau.

Il est important de noter que cela fonctionnera comme prévu pour les types primitifs (chaîne, nombre, etc.), et d'expliquer également le comportement attendu pour les types de référence ...

Si vous avez un tableau de types Reference, dites de type Object. Le tableau sera copié, mais les deux tableaux contiendront des références aux mêmes Object. Donc, dans ce cas, il semblerait que le tableau soit copié par référence même si le tableau est réellement copié.

jondavidjohn
la source
12
Non, ce ne serait pas une copie complète.
jondavidjohn
Essaye ça; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh
2
Quelle est la différence entre cette réponse et la réponse acceptée?
Isaac Pak
obtenir une erreur dans la console pour votre exemple donné "TypeError: window.addEvent n'est pas une fonction"
Ravi Sharma
72

Une alternative à sliceis concat, qui peut être utilisée de 2 manières. Le premier d'entre eux est peut-être plus lisible car le comportement souhaité est très clair:

var array2 = [].concat(array1);

La deuxième méthode est:

var array2 = array1.concat();

Cohen (dans les commentaires) a souligné que cette dernière méthode a de meilleures performances .

La façon dont cela fonctionne est que la concatméthode crée un nouveau tableau composé des éléments de l'objet sur lequel il est appelé, suivis des éléments de tous les tableaux qui lui sont passés en tant qu'arguments. Donc, quand aucun argument n'est passé, il copie simplement le tableau.

Lee Penkman, également dans les commentaires, les points que s'il y a une chance array1est undefined, vous pouvez retourner un tableau vide comme suit:

var array2 = [].concat(array1 || []);

Ou, pour la deuxième méthode:

var array2 = (array1 || []).concat();

Notez que vous pouvez aussi le faire avec slice: var array2 = (array1 || []).slice();.

Ninjakannon
la source
31
En fait, vous pouvez également faire: var array2 = array1.concat (); C'est beaucoup plus rapide en termes de performances. (JSPerf: jsperf.com/copy-simple-array et jsperf.com/copy-array-slice-vs-concat/5
Cohen
5
Il vaut la peine de noter que si array1 n'est pas un tableau, puis [].concat(array1)retourne [array1]par exemple si son indéfini vous obtiendrez [undefined]. Je fais parfoisvar array2 = [].concat(array1 || []);
Lee Penkman
60

Voici comment je l'ai fait après avoir essayé de nombreuses approches:

var newArray = JSON.parse(JSON.stringify(orgArray));

Cela créera une nouvelle copie complète non liée à la première (pas une copie superficielle).

De plus, cela ne clonera évidemment pas les événements et les fonctions, mais la bonne chose que vous pouvez le faire en une seule ligne, et il peut être utilisé pour tout type d'objet (tableaux, chaînes, nombres, objets ...)

Chtiwi Malek
la source
4
C'est le meilleur. J'utilise la même méthode il y a longtemps et je pense qu'il n'y a plus de sens dans les boucles récursives de la vieille école
Vladimir Kharlampidi
1
Sachez que cette option ne gère pas bien les structures de type graphique: se bloque en présence de cycles et ne conserve pas les références partagées.
Ruben
1
Cela échoue également pour des choses comme Date, ou en fait, tout ce qui a un prototype. De plus, undefineds est converti en nulls.
Dancrumb,
7
Personne n'est-il assez courageux pour commenter l'inefficacité flagrante du processeur et de la mémoire de la sérialisation en texte, puis de l'analyse vers un objet?
Lawrence Dol
3
Cette solution est la seule qui a fonctionné. L'utilisation de slice () est vraiment une fausse solution.
20

Certaines des méthodes mentionnées fonctionnent bien lorsque vous travaillez avec des types de données simples tels que nombre ou chaîne, mais lorsque le tableau contient d'autres objets, ces méthodes échouent. Lorsque nous essayons de passer un objet d'un tableau à un autre, il est passé en tant que référence, pas l'objet.

Ajoutez le code suivant dans votre fichier JavaScript:

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

Et utilisez simplement

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

Ça va marcher.

sarvesh singh
la source
2
je reçois cette erreur quand j'ajouter ce code à ma page « Uncaught RangeError: taille de la pile d'appel maximale dépassée »
Sawe
1
Mes excuses, cette erreur se produit dans le chrome si arr1 n'est pas déclaré. donc j'ai copié-collé le code ci-dessus, et j'obtiens l'erreur, cependant, si je déclare le tableau arr1, alors je n'obtiens pas l'erreur. Vous pourriez améliorer la réponse en déclarant arr1 juste au-dessus d'arr2, je vois qu'il y a pas mal de 'nous' là-bas qui n'ont pas reconnu que nous devions déclarer arr1 (en partie parce que lorsque j'évaluais votre réponse, j'étais pressé et avait besoin de quelque chose qui «fonctionne tout simplement»)
sawe
.slice()fonctionne toujours bien même si vous avez des objets dans votre tableau: jsfiddle.net/edelman/k525g
Jason
7
@Jason mais les objets pointent toujours vers le même objet, donc changer l'un changera l'autre. jsfiddle.net/k525g/1
Samuel
Excellent code. Une question que j'ai, j'ai en fait essayé de copier un tableau dans un autre comme celui-ci var arr1 = new Array () puis var arr2 = arr1; Si je change quelque chose dans arr2, le changement se produit également dans arr1. Cependant, si j'utilise le prototype de clone que vous avez créé, il crée en fait une nouvelle instance complète de ce tableau ou en d'autres termes, il le copie. Est-ce donc un problème de navigateur? ou javascript définit par défaut les deux variables en pointant l'une sur l'autre avec l'utilisation de pointeurs lorsque quelqu'un fait var arr2 = arr1 et pourquoi ne se produit-il pas avec des variables entières? voir jsfiddle.net/themhz/HbhtA
themhz
17

Depuis ES2015,

var arr2 = [...arr1];
Boopathi Rajaa
la source
17

Personnellement, je pense qu'Array.from est une solution plus lisible. Soit dit en passant, méfiez-vous de la prise en charge de son navigateur.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);
Lewis
la source
1
Oui, c'est très lisible. La .slice()solution n'est absolument pas intuitive. Merci pour cela.
Banago
15

Important!

La plupart des réponses ici fonctionnent pour des cas particuliers .

Si vous ne vous souciez pas de l'utilisation d'objets et d'accessoires profonds / imbriqués ( ES6 ):

let clonedArray = [...array]

mais si vous voulez faire un clone profond, utilisez plutôt ceci:

let cloneArray = JSON.parse(JSON.stringify(array))


Pour les utilisateurs de lodash:

let clonedArray = _.clone(array) Documentation

et

let clonedArray = _.cloneDeep(array) Documentation

ulou
la source
11

Si vous êtes dans un environnement ECMAScript 6 , en utilisant l' opérateur Spread, vous pouvez le faire de cette façon:

var arr1 = ['a','b','c'];
var arr2 = [...arr1]; //copy arr1
arr2.push('d');

console.log(arr1)
console.log(arr2)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>

Walter Chapilliquen - wZVanG
la source
9

Ajout à la solution de array.slice (); sachez que si vous avez des tableaux multidimensionnels, les sous-tableaux seront copiés par des références. Ce que vous pouvez faire est de boucler et de découper () chaque sous-tableau individuellement

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

les mêmes choses vont au tableau d'objets, ils seront copiés par référence, vous devez les copier manuellement

A.Zaben
la source
Cette réponse mérite une place en haut de la page! Je travaillais avec des sous-tableaux multidimensionnels et je ne pouvais pas comprendre pourquoi les tableaux intérieurs étaient toujours copiés par ref et non par val. Cette logique simple a résolu mon problème. Je vous donnerais +100 si possible!
Mac
8

Les valeurs primitives sont toujours transmises par sa valeur (copiée). Les valeurs composées sont cependant transmises par référence.

Alors, comment copions-nous cet arr?

let arr = [1,2,3,4,5];

Copier un tableau dans ES6

let arrCopy = [...arr]; 

Copier n tableau dans ES5

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

Pourquoi `laisser arrCopy = arr` ne passe pas par valeur?

Passer d'une variable à une autre sur des valeurs composées telles que Object / Array se comporte différemment. En utilisant l'opérateur asign sur les valeurs de copand, nous transmettons une référence à un objet. C'est pourquoi la valeur des deux tableaux change lors de la suppression / ajout d'éléments arr.

Exceptions:

arrCopy[1] = 'adding new value this way will unreference';

Lorsque vous affectez une nouvelle valeur à la variable, vous modifiez la référence elle-même et cela n'affecte pas l'objet / tableau d'origine.

Lire la suite

DevWL
la source
6

Comme nous le savons en Javascript, les tableaux et les objets sont par référence, mais comment pouvons-nous faire pour copier le tableau sans changer le tableau d'origine plus tard?

Voici quelques façons de procéder:

Imaginez que nous ayons ce tableau dans votre code:

var arr = [1, 2, 3, 4, 5];

1) Boucle à travers le tableau dans une fonction et retourne un nouveau tableau, comme ceci:

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }

2) En utilisant la méthode de tranche, la tranche est pour couper une partie du tableau, elle coupera une partie de votre tableau sans toucher l'original, dans la tranche, si ne spécifiez pas le début et la fin du tableau, elle coupera le tout tableau et essentiellement faire une copie complète du tableau, afin que nous puissions facilement dire:

var arr2 = arr.slice(); // make a copy of the original array

3) Également la méthode de contact, c'est pour fusionner deux tableaux, mais nous pouvons simplement spécifier l'un des tableaux, puis cela fait essentiellement une copie des valeurs dans le nouveau tableau contacté:

var arr2 = arr.concat();

4) Également la méthode stringify et parse, ce n'est pas recommandé, mais peut être un moyen facile de copier un tableau et des objets:

var arr2 = JSON.parse(JSON.stringify(arr));

5) Méthode Array.from, ce n'est pas largement pris en charge, avant utilisation, vérifiez le support dans différents navigateurs:

const arr2 = Array.from(arr);

6) Manière ECMA6, également pas entièrement prise en charge, mais les babelJs peuvent vous aider si vous souhaitez transpiler:

const arr2 = [...arr];
Alireza
la source
6
let a = [1,2,3];

Vous pouvez maintenant effectuer l'une des opérations suivantes pour créer une copie d'un tableau.

let b = Array.from(a); 

OU

let b = [...a];

OU

let b = new Array(...a); 

OU

let b = a.slice(); 

OU

let b = a.map(e => e);

Maintenant, si je change un,

a.push(5); 

Alors, a est [1,2,3,5] mais b est toujours [1,2,3] car il a une référence différente.

Mais je pense que dans toutes les méthodes ci-dessus, Array.from est meilleur et fait principalement pour copier un tableau.

Nitesh Ranjan
la source
1
lequel est le plus rapide?
Marc Frame
5

Je préférerais personnellement cette façon:

JSON.parse(JSON.stringify( originalObject ));
Diemauerdk
la source
Donc, la manière suggérée ici ?
Script47
4

Dans mon cas particulier, je devais m'assurer que la baie restait intacte, cela a donc fonctionné pour moi:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}
Brent Keller
la source
2
+1 pour ne pas ajouter d'obscurité à votre code en appelant une fonction qui fait exactement la même chose, mais de manière moins évidente. slice peut être plus efficace sous le capot, mais pour toute personne travaillant sur le code, cela montre votre intention. de plus, il est plus facile d'optimiser plus tard, si vous voulez (par exemple) filtrer ce que vous copiez. notez cependant que cela ne gère pas la copie en profondeur, et les mêmes objets internes sont passés au nouveau tableau, par référence. C'est peut-être ce que vous voulez faire, peut-être pas.
non synchronisé
4

Faites une copie du tableau / objet multidimensionnel:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

Merci à James Padolsey pour cette fonction.

Source: ici

encre
la source
3

Dan, pas besoin d'utiliser des astuces sophistiquées. Il vous suffit de faire une copie de arr1 en procédant ainsi.

var arr2 = new Array(arr1);

Maintenant arr1et arr2sont deux variables de tableau différentes stockées dans des piles distinctes. Vérifiez cela sur jsfiddle .

DragoRaptor
la source
Cela ne copie pas le tableau. Il crée un tableau avec un élément qui fait référence à l'original (ie var arr2 = [arr1];).
Timothy003
3

Lorsque nous voulons copier un tableau à l'aide de l'opérateur d'affectation ( =), il ne crée pas de copie, il copie simplement le pointeur / la référence sur le tableau. Par exemple:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

Souvent, lorsque nous transformons des données, nous voulons conserver intacte notre infrastructure de données initiale (par exemple, un tableau). Nous le faisons en faisant une copie exacte de notre tableau afin que celui-ci puisse être transformé tandis que celui initial reste intact.

Façons de copier un tableau:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
	const newArray = [];
    for(let i= 0; i < base.length; i++) {
	    newArray[i] = base[i];
	}
	return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

Soyez prudent lorsque des tableaux ou des objets sont imbriqués!:

Lorsque les tableaux sont imbriqués, les valeurs sont copiées par référence. Voici un exemple de la façon dont cela pourrait entraîner des problèmes:

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

N'utilisez donc pas ces méthodes lorsqu'il y a des objets ou des tableaux dans votre tableau que vous souhaitez copier. c'est-à-dire n'utilisez ces méthodes que sur des tableaux de primitives.

Si vous voulez cloner en profondeur un tableau javascript utilisé JSON.parseconjointement avec JSON.stringify, comme ceci:

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

Performance de copie:

Alors, lequel choisissons-nous pour des performances optimales. Il s'avère que la méthode la plus verbeuse, la forboucle a les performances les plus élevées. Utilisez lefor boucle pour une copie très gourmande en CPU (grandes / nombreuses baies).

Après cela, la .slice()méthode a également des performances décentes et est également moins détaillée et plus facile à implémenter pour le programmeur. Je suggère d'utiliser .slice()pour votre copie quotidienne des tableaux qui ne sont pas très gourmands en ressources processeur. Évitez également d'utiliser le JSON.parse(JSON.stringify(arr))(beaucoup de surcharge) si aucun clone profond n'est requis et que les performances sont un problème.

Test de performance de la source

Willem van der Veen
la source
3

Si votre tableau contient des éléments du type de données primitif tels que int, char ou string, etc., vous pouvez utiliser l'une de ces méthodes qui renvoie une copie du tableau d'origine, telle que .slice () ou .map () ou l'opérateur d'étalement ( grâce à ES6).

new_array = old_array.slice()

ou

new_array = old_array.map((elem) => elem)

ou

const new_array = new Array(...old_array);

MAIS si votre tableau contient des éléments complexes tels que des objets (ou des tableaux) ou des objets plus imbriqués , alors, vous devrez vous assurer que vous faites une copie de tous les éléments du niveau supérieur au dernier niveau sinon référence de l'intérieur les objets seront utilisés et cela signifie que la modification des valeurs dans les éléments d'objet dans new_array affectera toujours old_array. Vous pouvez appeler cette méthode de copie à chaque niveau comme faisant une COPIE PROFONDE de old_array.

Pour la copie en profondeur, vous pouvez utiliser les méthodes susmentionnées pour les types de données primitifs à chaque niveau en fonction du type de données ou vous pouvez utiliser cette méthode coûteuse (mentionnée ci-dessous) pour effectuer une copie en profondeur sans faire beaucoup de travail.

var new_array = JSON.parse(JSON.stringify(old_array));

Il existe de nombreuses autres méthodes que vous pouvez utiliser en fonction de vos besoins. Je n'ai mentionné que certains d'entre eux pour donner une idée générale de ce qui se passe lorsque nous essayons de copier un tableau dans l'autre par valeur .

abhinav1602
la source
Merci beaucoup, votre réponse a été la seule à avoir fonctionné pour mon scénario,
albert sh
2

Si vous souhaitez créer une nouvelle copie d'un objet ou d'un tableau, vous devez copier explicitement les propriétés de l'objet ou des éléments du tableau, par exemple:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

Vous pouvez rechercher plus d'informations sur Google sur les valeurs primitives immuables et les références d'objets mutables.

Sotiris
la source
1
Vous n'avez pas à copier explicitement les propriétés des objets du tableau. Voir la réponse de Chtiwi Malek.
Magne
2

L'utilisation de la copie complète de jQuery peut être effectuée comme suit:

var arr2 = $.extend(true, [], arr1);
Oleksandr Tsurika
la source
2

Vous pouvez également utiliser l'opérateur d'étalement ES6 pour copier le tableau

var arr=[2,3,4,5];
var copyArr=[...arr];
ankur kushwaha
la source
2

Voici quelques autres façons de copier:

const array = [1,2,3,4];

const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );

ganesh phirke
la source
2

Exemples rapides:

  1. Si les éléments du tableau sont des types primitifs (chaîne, nombre, etc.)

var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in 
// different places in the memory
var arr2 = arr1.slice(); 
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]

  1. Si les éléments du tableau sont des littéraux d'objet, un autre tableau ({}, [])

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice(); 
arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                 // deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                 // pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9;	 // not OK - same above reason

console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

  1. Solution pour 2 : Deep Copy par élément par élément

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop();	  // OK - don't affect arr1
arr2[0].x = 'z';  // OK - don't affect arr1
arr2[1][0] = 9;	  // OK - don't affect arr1

console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

SridharKritha
la source
1

Voici une variante:

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */
Zyox
la source
ce n'est pas une si mauvaise idée, même si je ferais mieux d'utiliser JSON stringify / parse au lieu d'eval, et encore une autre comparaison jsPerf serait bien à vérifier, notez également que ce toSourcen'est pas standard et ne fonctionnera pas dans Chrome par exemple.
dmi3y
1

Il y a le nouveau Array.from, mais malheureusement, au moment de la rédaction de ce document, il n'est pris en charge que sur les versions récentes de Firefox (32 et supérieures). Il peut être simplement utilisé comme suit:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

Référence: ici

Ou Array.prototype.mappeut être utilisé avec une fonction d'identité:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Référence: ici

Ashraf Sabry
la source
+1 pour la mention Array.from, qui est désormais pris en charge sur tous les principaux navigateurs, à l'exception d'Internet Explorer ( source ). .
mgthomas99
Soyez conscient de la Array.frommutation des données, ne fournit pas de copie / clonage en profondeur.
Sudhansu Choudhary
1

Pour les tableaux contenant des objets ES6

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
askilondz
la source