web-dev-qa-db-fra.com

Copier le tableau par valeur

Lors de la copie d'un tableau en JavaScript sur 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 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?

1501
Dan

Utilisez ceci:

var newArray = oldArray.slice();

Fondamentalement, l'opération slice() 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 pour l'objet réel), slice() copie les références d'objet dans le nouveau tableau. Le tableau d'origine et le nouveau tableau font référence au même objet. Si un objet référencé change, les modifications sont visibles à la fois pour les baies nouvelles et originales.

Les primitives telles que les chaînes et les nombres étant immuables, il est impossible de modifier la chaîne ou le nombre. 

2408
Saket

En Javascript, les techniques de copie en profondeur 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 en profondeur dépendent des trois types de tableau

En fonction des types d'éléments du tableau, nous pouvons utiliser différentes techniques pour effectuer une copie en profondeur.

Javascript deep copy techniques by element types

  • Tableau de valeurs littérales (type1)
    Les techniques [...myArray], myArray.splice(0), myArray.slice() et myArray.concat() peuvent être utilisées pour copier en profondeur des tableaux contenant des valeurs littérales (booléen, nombre et chaîne) uniquement; 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 de structures littérales (type2)
    La technique JSON.parse(JSON.stringify(myArray)) peut être utilisée pour copier en profondeur des valeurs littérales (boolean, number, string) et des structures littérales (tableau, objet), mais pas des objets prototypes.

  • Tous les tableaux (type1, type2, type3)
    La technique jQuery $.extend(myArray) peut être utilisée pour copier en profondeur tous les types de tableaux. Des bibliothèques comme trait de soulignement _ et Lo-dash offrent des fonctions de copie profonde similaires à jQuery$.extend(), mais ont des performances inférieures. Plus surprenant, $.extend() a des performances supérieures à la technique JSON.parse(JSON.stringify(myArray))http://jsperf.com/js-deep-copy/15 .
    Et pour les développeurs qui craignent les bibliothèques tierces (telles que jQuery), vous pouvez utiliser la fonction personnalisée suivante: qui a des performances plus élevées que $ .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é que arr1 est un tableau de valeurs littérales (booléen, nombre ou chaîne), vous pouvez utiliser n’importe quelle technique de copie en profondeur décrite ci-dessus, dans laquelle l’opérateur étendu ... 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
435
tfmontague

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

const itemsCopy = [...items];

Aussi, si vous voulez 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 maintenant pris en charge par tous les principaux navigateurs mais si vous avez besoin d'un support plus ancien, utilisez TypeScript ou babel et compilez-le sur ES5.

Plus d'infos sur les spreads

169
Luke Femur

Pas besoin de jQuery ... Exemple de travail

var arr2 = arr1.slice()

Cela permet de copier le tableau de la position de départ 0 à 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's. Donc, dans ce cas, il semblerait que le tableau soit copié par référence même si le tableau est en fait copié.

150
jondavidjohn

Une alternative à slice est concat , qui peut être utilisée de 2 manières. Le premier 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 méthode concat crée un nouveau tableau composé des éléments de l'objet sur lequel elle est appelée, suivie des éléments de tous les tableaux transmis comme arguments. Donc, quand aucun argument n'est passé, il copie simplement le tableau.

Lee Penkman, également dans les commentaires, souligne que s'il existe une chance que array1 soit 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 également le faire avec slice: var array2 = (array1 || []).slice();.

70
Ninjakannon

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

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

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

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

51
Chtiwi Malek

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é comme référence et non comme 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 simplement utiliser

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

Ça va marcher.

18
sarvesh singh

À partir de ES2015,

var arr2 = [...arr1];
15
Boopathi Rajaa

Personnellement, je pense que Array.from est une solution plus lisible. 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);
14
Lewis

Si vous êtes dans un environnement de ECMAScript 6 , en utilisant l'opérateur Spread , vous pouvez le faire comme suit:

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>

Ajout à la solution de array.slice (); sachez que si vous avez un tableau multidimensionnel les sous-tableaux seront copiés par références . Ce que vous pouvez faire est de boucler et de slice () 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 à tableau d'objets, ils seront copiés par référence, vous devez les copier manuellement

9
A.Zaben

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

Voici quelques façons de le faire:

Imaginez que nous ayons ce tableau dans votre code:

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

1) Faire une boucle à travers le tableau dans une fonction et retourner 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 slice, slice est utilisé pour trancher une partie du tableau. Il tranchera une partie de votre tableau sans toucher l'original. tableau et faire essentiellement une copie complète du tableau, de sorte que nous pouvons facilement dire:

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

3) La méthode contact est également utilisée pour fusionner deux tableaux, mais nous pouvons simplement spécifier l’un des tableaux, puis faire une copie des valeurs du nouveau tableau contacté:

var arr2 = arr.concat();

4) Aussi, stringify et méthode d'analyse, 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, celle-ci n’est pas largement prise en charge. Avant toute utilisation, vérifiez la prise en charge dans différents navigateurs:

const arr2 = Array.from(arr);

6) Manière ECMA6, pas complètement supportée, mais babelJs peut vous aider si vous voulez transpiler:

const arr2 = [...arr];
6
Alireza

Faire une copie d'un 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

4
ink

Dans mon cas particulier, je devais m'assurer que le tableau restait intact pour que cela fonctionne 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]);
}
4
Brent Keller

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 souhaitons conserver notre structure de données initiale (par exemple, Array). Nous faisons cela en faisant une copie exacte de notre tableau afin que celui-ci puisse être transformé tandis que le premier 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);

Faites attention 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 montrant comment 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

Par conséquent, n'utilisez pas ces méthodes lorsque vous souhaitez copier des objets ou des tableaux dans votre tableau. C'est-à-dire que vous utilisez ces méthodes sur des tableaux de primitives uniquement. 

Si vous voulez créer un tableau javascript en profondeur, utilisez JSON.parse conjointement 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 détaillée, la boucle for, est la plus performante. Utilisez la boucle for pour une copie réellement intensive en ressources CPU (grands/nombreux tableaux). 

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

Test de performance source

3

Voici quelques autres moyens 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 );

2
ganesh phirke

Exemples rapides:

  1. Si les éléments du tableau sont 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 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 for 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 ] ]

2
SridharKritha

La copie en profondeur de jQuery peut être réalisée comme suit:

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

Vous pouvez également utiliser l'opérateur de propagation ES6 pour copier un tableau

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

Si votre tableau contient des éléments du type {type de données primitif} tels que int, char, ou chaîne, etc., vous pouvez utiliser l'une de ces méthodes qui renverra une copie du tableau d'origine, tel que. opérateur slice () ou .map () ou spread (grâce à ES6).

new_array = old_array.slice()

ou

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

ou

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

BUT _ si votre tableau contient éléments complexes tels que des objets (ou des tableaux) ou plus objets imbriqués, vous devrez alors vous assurer que vous êtes bien faire une copie de tous les éléments du niveau le plus élevé au dernier niveau, sinon la référence des objets internes sera utilisée, ce qui signifie que la modification des valeurs dans object_elements dans new_array aura toujours une incidence sur old_array. Vous pouvez appeler cette méthode de copie à chaque niveau en effectuant une COPIE PROFOND De old_array.

Pour la copie en profondeur, vous pouvez utiliser les méthodes mentionnées ci-dessus pour les types de données primitifs à chaque niveau, en fonction du type de données. Vous pouvez également utiliser cette méthode coûteuse (mentionnée ci-dessous) pour effectuer une copie en profondeur sans trop en faire. 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’en ai mentionné que quelques-uns pour avoir donné une idée générale de ce qui se passe lorsque nous essayons de copier un tableau dans l’autre par la valeur.

2
Abhinav1602

Si vous souhaitez créer une nouvelle copie d'un objet ou d'un tableau, vous devez explicitement copier les propriétés de l'objet ou les é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 davantage d'informations sur Google concernant les valeurs primitives immuables et les références d'objets modifiables.

2
Sotiris

Personnellement, je préférerais cette façon JSON.parse (JSON.stringify (originalObject));

1
Diemauerdk

Vous pouvez utiliser ES6 avec spread Operator, c'est plus simple.

arr2 = [...arr1];

Il y a des limitations..check docs Spread syntax @ mozilla

1
BluePie

Voici comment vous pouvez le faire pour un tableau de tableaux de primitives de profondeur variable:

// If a is array: 
//    then call cpArr(a) for each e;
//    else return a

const cpArr = a => Array.isArray(a) && a.map(e => cpArr(e)) || a;

let src = [[1,2,3], [4, ["five", "six", 7], true], 8, 9, false];
let dst = cpArr(src);

https://jsbin.com/xemazog/edit?js,console

1
G.Denis

Il y a le Array.from nouvellement introduit, mais malheureusement, au moment d'écrire ces lignes, il n'est supporté que par les versions récentes de Firefox (32 et plus). 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.map peut ê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

1
Ashraf Sabry
let a = [1,2,3];

Vous pouvez maintenant utiliser l’une des méthodes suivantes pour copier un tableau.

let b = Array.from(a); 

OR

let b = [...a];

OR

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

OR

let b = a.slice(); 

OR

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, dans toutes les méthodes ci-dessus} _ Array.fromest meilleur et fait principalement pour copier un tableau.

1
Nitesh Ranjan

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
 */
1
Zyox

Je viens d'écrire:

arr2 = arr1.concat();

Vous générez un nouveau tableau avec une copie du premier. Sachez qu’il s’agit d’un moyen fonctionnel d’enfoncer un élément dans le tableau.

Si votre code est basé sur ES6, vous pouvez également utiliser l'opérateur spread:

arr2 = [...arr1];
0
alejoko

Pour un tableau ES6 contenant des objets

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

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

Alors, comment pouvons-nous copier 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 `let 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 copand, nous transmettons la référence à un objet. C'est pourquoi les valeurs des deux tableaux changent lors de la suppression/l'ajout d'éléments arr.

Exceptios: 

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/le tableau d'origine. 

Lire la suite

0
DevWL