Copiar matriz por valor

1745

Ao copiar uma matriz em JavaScript para outra matriz:

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

Percebi que isso arr2se refere à mesma matriz que arr1, em vez de uma nova matriz independente. Como copiar a matriz para obter duas matrizes independentes?

Dan
fonte
3
Parece que atualmente no Chrome 53 e Firefox 48 temos desempenho slicee spliceoperações interessantes e novo operador de propagação e Array.fromimplementação muito mais lenta. Veja perfjs.fnfo
Pencroff
jsben.ch/#/wQ9RU <= esta referência dá uma visão geral sobre as diferentes maneiras de copiar um array
EscapeNetscape
Para essa matriz (que contém cadeias primitivas), pode utilizar var arr2 = arr1.splice();a cópia profunda, mas esta técnica não vai funcionar se os elementos em sua matriz contêm estruturas literais (ie []ou {}) ou objetos de protótipo (ou seja function () {}, new, etc). Veja minha resposta abaixo para obter mais soluções.
tfmontague
16
É 2017, de modo que você pode considerar o uso de ES6 apresenta: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Hinrich

Respostas:

2701

Usa isto:

var newArray = oldArray.slice();

Basicamente, a slice()operação clona a matriz e retorna uma referência a uma nova matriz.

Observe também que:

Para referências, seqüências de caracteres e números (e não o objeto real), slice()copia as referências do objeto para a nova matriz. Tanto a matriz original quanto a nova se referem ao mesmo objeto. Se um objeto referenciado for alterado, as alterações serão visíveis para as matrizes nova e original.

Primitivas como seqüências de caracteres e números são imutáveis, portanto, alterações na sequência ou no número são impossíveis.

Saket
fonte
9
Com relação ao desempenho, os seguintes testes jsPerf mostram que var arr2 = arr1.slice () é tão rápido quanto var arr2 = arr1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 e jsperf.com/copy-simple-array . O resultado de jsperf.com/array-copy/5 meio que me surpreendeu ao ponto que me pergunto se o código de teste é válido.
Cohen
94
Mesmo que isso já tenha recebido muitos upvotes, merece outro porque descreve adequadamente as referências em JS, o que é meio raro, infelizmente.
Wayne
34
@ GáborImre você adicionaria uma biblioteca inteira simplesmente para facilitar a leitura? Mesmo? Eu apenas adicionaria um comentário se estivesse preocupado com a legibilidade. Veja: var newArray = oldArray.slice (); // Clona oldArray para newArray
dudewad 08/02
12
@ GáborImre Eu entendo isso, com certeza. Mas responder a um problema de engenharia específico incluindo uma biblioteca inteira na minha opinião não é útil, é um inchaço no design. Eu vejo desenvolvedores fazendo muito isso, e então você acaba com um projeto que inclui uma estrutura inteira para substituir a necessidade de escrever uma única função. Apenas meu MO, no entanto.
Dudewad
5
Lição aprendida: não confunda .slice()com .splice(), o que fornece uma matriz vazia. Grande diferença.
28518 crazypeter
531

Em Javascript, as técnicas de cópia em profundidade dependem dos elementos em uma matriz. Vamos começar por aí.

Três tipos de elementos

Os elementos podem ser: valores literais, estruturas literais ou protótipos.

// 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 () {}`

A partir desses elementos, podemos criar três tipos de matrizes.

// 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 () {}];

As técnicas de cópia profunda dependem dos três tipos de matriz

Com base nos tipos de elementos da matriz, podemos usar várias técnicas para copiar em profundidade.

Técnicas de cópia profunda de Javascript por tipos de elementos

  • Matriz de valores de literais (de tipo 1)
    O [...myArray], myArray.splice(0), myArray.slice(), e myArray.concat()técnicas podem ser utilizadas para matrizes de cópia profundos com valores literais (booleano, número, e de cordas) apenas; onde o operador Spread [...myArray]tem o melhor desempenho ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).

  • Matriz de valores literais (tipo1) e estruturas literais (tipo2)
    A JSON.parse(JSON.stringify(myArray))técnica pode ser usada para copiar profundamente valores literais (booleano, número, sequência) e estruturas literais (matriz, objeto), mas não objetos de protótipo.

  • Todas as matrizes (tipo1, tipo2, tipo3)
    A $.extend(myArray)técnica jQuery pode ser usada para copiar em profundidade todos os tipos de matriz. Bibliotecas como Underscore e Lo-dash oferecem funções de cópia profunda semelhantes ao jQuery $.extend() , mas com desempenho inferior. Surpreendentemente, $.extend()tem desempenho superior à JSON.parse(JSON.stringify(myArray))técnica http://jsperf.com/js-deep-copy/15 .
    E para os desenvolvedores que evitam as bibliotecas de terceiros (como o jQuery), você pode usar a seguinte função personalizada; que tem um desempenho superior a $ .extend e copia em profundidade todas as matrizes.

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;
}

Então, para responder à pergunta ...

Questão

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

Percebi que arr2 se refere à mesma matriz que arr1, em vez de uma nova matriz independente. Como copiar a matriz para obter duas matrizes independentes?

Responda

Como arr1é uma matriz de valores literais (booleano, número ou string), você pode usar qualquer técnica de cópia em profundidade discutida acima, em que o operador de spread ...tenha o melhor desempenho.

// 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
fonte
1
Muitas dessas abordagens não funcionam bem. Usar o operador de atribuição significa que você deve reatribuir o valor literal original de arr1. É muito raro que seja esse o caso. Usando spliceobliterados arr1, isso não é uma cópia. O uso JSONfalhará se algum dos valores da matriz for Funções ou possuir protótipos (como a Date).
Dancrumb 18/09/14
Usar emenda é uma solução parcial. Ele falhará em muito mais casos que o JSON. O Splice cria uma cópia profunda de seqüências e números, quando move valores - nunca disse que retorna uma cópia.
Tfmontague
1
Por que emenda (0)? Não deveria ser fatia ()? Eu acho que não deve modificar a matriz original, o que emenda. @JamesMontagne
helpse
2
emenda criará ponteiros para os elementos na matriz original (cópia superficial). splice (0) alocará nova memória (cópia profunda) para elementos na matriz que são números ou seqüências de caracteres e criará ponteiros para todos os outros tipos de elementos (cópia superficial). Ao passar um valor inicial de zero para o método da função de emenda, ele não divide nenhum elemento da matriz original e, portanto, não o modifica.
Tfmontague
1
Na verdade, existe apenas um tipo de matriz: uma matriz de "algumas coisas". Não há diferença entre [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]e qualquer outra matriz.
Wizzwizz4
189

Você pode usar spreads de matriz ...para copiar matrizes.

const itemsCopy = [...items];

Além disso, se desejar criar uma nova matriz com a existente, faça parte dela:

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

As propagações de matriz agora são suportadas em todos os principais navegadores, mas se você precisar de suporte mais antigo, use typescript ou babel e compile no ES5.

Mais informações sobre spreads

Luke Femur
fonte
1
Isso não funcionará para cópias profundas. Clone profundamente uma matriz em JavaScript .
Senão
151

Não é necessário jQuery ... Exemplo de trabalho

var arr2 = arr1.slice()

Isso copia a matriz da posição inicial 0até o final da matriz.

É importante observar que funcionará conforme o esperado para tipos primitivos (sequência, número etc.) e também explicar o comportamento esperado para tipos de referência ...

Se você tiver uma matriz de tipos de referência, digamos do tipo Object. A matriz será copiada, mas ambas as matrizes conterão referências às mesmas Object. Portanto, nesse caso, parece que a matriz é copiada por referência, mesmo que a matriz seja realmente copiada.

jondavidjohn
fonte
12
Não, isso não seria uma cópia profunda.
jondavidjohn
Tente isso; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh
2
Qual é a diferença entre esta resposta e a resposta aceita?
Isaac Pak
recebendo erro no console para o seu exemplo dado "TypeError: window.addEvent não é uma função"
Ravi Sharma
72

Uma alternativa para sliceé concat, que pode ser usada de duas maneiras. O primeiro deles é talvez mais legível, pois o comportamento pretendido é muito claro:

var array2 = [].concat(array1);

O segundo método é:

var array2 = array1.concat();

Cohen (nos comentários) apontou que este último método tem melhor desempenho .

A maneira como isso funciona é que o concatmétodo cria uma nova matriz que consiste nos elementos no objeto no qual é chamado, seguido pelos elementos de quaisquer matrizes passadas a ele como argumentos. Portanto, quando nenhum argumento é passado, ele simplesmente copia a matriz.

Lee Penkman, também nos comentários, assinala que, se há uma chance array1é undefined, você pode retornar um array vazio como se segue:

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

Ou, para o segundo método:

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

Note que você também pode fazer isso com slice: var array2 = (array1 || []).slice();.

Ninjakannon
fonte
31
Na verdade, você também pode fazer: var array2 = array1.concat (); É muito mais rápido em relação ao desempenho. (JSPerf: jsperf.com/copy-simple-array e jsperf.com/copy-array-slice-vs-concat/5
Cohen
5
Vale a pena notar que, se array1 não for um array, [].concat(array1)retornará, [array1]por exemplo, se não for definido, você receberá [undefined]. Às vezes eu façovar array2 = [].concat(array1 || []);
lee penkman
60

Foi assim que fiz depois de tentar várias abordagens:

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

Isso criará uma nova cópia profunda não relacionada à primeira (não uma cópia superficial).

Além disso, obviamente, isso não clonará eventos e funções, mas o bom é que você pode fazê-lo em uma linha e pode ser usado para qualquer tipo de objeto (matrizes, strings, números, objetos ...)

Chtiwi Malek
fonte
4
Este é o melhor. Eu uso o mesmo método há muito tempo e acho que não há mais sentido em loops recursivo velha escola
Vladimir Kharlampidi
1
Esteja ciente de que esta opção não lida com estruturas semelhantes a gráficos: trava na presença de ciclos e não preserva referências compartilhadas.
Ruben
1
Isso também falha em coisas como Date, ou de fato, qualquer coisa que tenha um protótipo. Além disso, undefineds são convertidos em nulls.
Dancrumb 18/09/14
7
Ninguém é corajoso o suficiente para comentar sobre a ineficiência bruta na CPU e na memória de serializar para texto e depois analisar de volta para um objeto?
Lawrence Dol
3
Esta solução é a única que funcionou. Usar o slice () é realmente uma solução falsa.
20

Alguns dos métodos mencionados funcionam bem ao trabalhar com tipos de dados simples, como número ou string, mas quando a matriz contém outros objetos, esses métodos falham. Quando tentamos passar qualquer objeto de uma matriz para outra, ele é passado como referência, não como objeto.

Adicione o seguinte código ao seu arquivo 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;
};

E simplesmente use

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

Vai funcionar.

sarvesh singh
fonte
2
Eu recebo esse erro quando adiciono este código à minha página 'RangeError não capturado: tamanho máximo da pilha de chamadas excedido'
sawe
1
Lamentamos, este erro ocorre no chrome se arr1 não for declarado. então eu copiei o código acima e recebo o erro, no entanto, se eu declarar o array arr1, não recebo o erro. Você pode melhorar a resposta declarando arr1 logo acima de arr2; vejo que existem muitos de nós que não reconheceram que precisávamos declarar arr1 (em parte porque, quando eu estava avaliando sua resposta, estava com pressa e precisava de algo que 'simplesmente funciona')
Sawe
.slice()ainda funciona bem mesmo se você tiver objetos em sua matriz: jsfiddle.net/edelman/k525g
Jason
7
@ Jason, mas os objetos ainda estão apontando para o mesmo objeto, portanto, mudar um mudará o outro. jsfiddle.net/k525g/1
Samuel
Código excelente. Uma pergunta que tenho, na verdade, tentei copiar uma matriz para outra como esta var arr1 = new Array () e depois var arr2 = arr1; Se eu mudar alguma coisa em arr2, a alteração também acontece com arr1. No entanto, se eu usar o protótipo de clone que você criou, ele cria uma nova instância completa dessa matriz ou, em outras palavras, a copia. Então, isso é um problema no navegador? ou javascript, por padrão, define as duas variáveis, uma apontando para a outra com o uso de ponteiros quando alguém faz var arr2 = arr1 e por que não acontece com variáveis ​​inteiras? Veja jsfiddle.net/themhz/HbhtA
themhz
17

A partir do ES2015,

var arr2 = [...arr1];
Boopathi Rajaa
fonte
17

Pessoalmente, acho que o Array.from é uma solução mais legível. A propósito, apenas tome cuidado com o suporte ao navegador.

//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
fonte
1
Sim, isso é muito legível. A .slice()solução é completamente não intuitiva. Obrigado por isso.
Banago 27/07/16
15

Importante!

A maioria das respostas aqui funciona para casos particulares .

Se você não se importa com objetos profundos / aninhados e adereços, use ( ES6 ):

let clonedArray = [...array]

mas se você quiser fazer um clone profundo, use isso:

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


Para usuários do lodash:

let clonedArray = _.clone(array) documentação

e

let clonedArray = _.cloneDeep(array) documentação

ulou
fonte
11

Se você estiver em um ambiente do ECMAScript 6 , usando o Operador de propagação, poderá fazê-lo desta maneira:

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
fonte
9

Adicionando à solução de array.slice (); esteja ciente de que se você tiver sub-matrizes multidimensionais de matriz serão copiadas por referências. O que você pode fazer é fazer um loop e fatiar () cada sub-matriz individualmente

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]);

o mesmo acontece com a matriz de objetos, eles serão copiados por referência, você deve copiá-los manualmente

A.Zaben
fonte
Esta resposta merece um lugar perto do topo da página! Eu estava trabalhando com sub-matrizes multidimensionais e não conseguia entender por que as matrizes internas estavam sempre sendo copiadas por ref e não por val. Essa lógica simples resolveu meu problema. Eu daria a você +100, se possível!
16283 Mac
8

Os valores primitivos sempre passam pelo seu valor (copiado). Os valores compostos, no entanto, são passados ​​por referência.

Então, como copiamos essa arr?

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

Copiar uma matriz no ES6

let arrCopy = [...arr]; 

Copiar n Matriz no ES5

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

Por que deixar arrCopy = arr não passar por valor?

Passar uma variável para outra em valores compostos, como Objeto / Matriz, se comporta de maneira diferente. Usando um operador asign em valores de copand, passamos referência a um objeto. É por isso que o valor de ambas as matrizes está mudando ao remover / adicionar elementos arr.

Exceções:

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

Ao atribuir um novo valor à variável, você está alterando a própria referência e ela não afeta o Objeto / Matriz original.

consulte Mais informação

DevWL
fonte
6

Como sabemos em matrizes e objetos Javascript são por referência, mas de que maneiras podemos copiar a matriz sem alterar a matriz original posteriormente?

Aqui estão algumas maneiras de fazer isso:

Imagine que temos essa matriz em seu código:

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

1) Fazendo loop na matriz em uma função e retornando uma nova matriz, assim:

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

2) Usando o método de fatia, fatia é para fatiar parte da matriz, fatiará parte de sua matriz sem tocar no original, na fatia, se não especificar o início e o fim da matriz, fatiará todo matriz e basicamente fazer uma cópia completa da matriz, para que possamos dizer facilmente:

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

3) Também entre em contato com o método, isto é para mesclar duas matrizes, mas podemos apenas especificar uma das matrizes e, basicamente, fazer uma cópia dos valores na nova matriz contatada:

var arr2 = arr.concat();

4) Também método stringify e parse, não é recomendado, mas pode ser uma maneira fácil de copiar Array e Objetos:

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

5) Método Array.from, isso não é amplamente suportado, antes de usar, verifique o suporte em diferentes navegadores:

const arr2 = Array.from(arr);

6) Maneira ECMA6, também não totalmente suportada, mas o babelJs pode ajudá-lo se você deseja transpilar:

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

Agora você pode executar qualquer um dos seguintes procedimentos para fazer uma cópia de uma matriz.

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);

Agora, se eu mudar um,

a.push(5); 

Então, a é [1,2,3,5], mas b ainda é [1,2,3], pois possui referência diferente.

Mas eu acho que, em todos os métodos acima de Array.from, é melhor e feito principalmente para copiar uma matriz.

Nitesh Ranjan
fonte
1
qual é o mais rápido?
7898 Marc
5

Eu pessoalmente prefiro assim:

JSON.parse(JSON.stringify( originalObject ));
Diemauerdk
fonte
Então, o caminho sugerido aqui ?
Script47
4

No meu caso particular, eu precisava garantir que a matriz permanecesse intacta, de modo que funcionou para mim:

// 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
fonte
2
+1 por não adicionar obscuridade ao seu código chamando uma função que faz exatamente a mesma coisa, mas de uma maneira menos óbvia. Uma fatia pode ser mais eficiente, mas para quem trabalha no código, isso mostra sua intenção. além disso, facilita a otimização mais tarde, se você quiser (por exemplo) filtrar o que está copiando. observe, no entanto, que isso não trata de cópia profunda e os mesmos objetos internos são passados ​​para a nova matriz, por referência. Pode ser o que você quer fazer, ou não.
não sincronizado
4

Faça uma cópia da matriz / objeto multidimensional:

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;
}

Obrigado a James Padolsey por esta função.

Fonte: Aqui

tinta
fonte
3

Dan, não há necessidade de usar truques sofisticados. Tudo o que você precisa fazer é copiar o arr1 fazendo isso.

var arr2 = new Array(arr1);

Agora arr1e arr2existem duas variáveis ​​de matriz diferentes armazenadas em pilhas separadas. Verifique isso no jsfiddle .

DragoRaptor
fonte
Isso não copia a matriz. Ele cria uma matriz com um elemento que faz referência ao original (ou seja var arr2 = [arr1];).
precisa saber é o seguinte
3

Quando queremos copiar um array usando o operador de atribuição ( =), ele não cria uma cópia, apenas copia o ponteiro / referência ao array. Por exemplo:

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

Frequentemente, quando transformamos dados, queremos manter intacta nossa estrutura de dados inicial (por exemplo, Matriz). Fazemos isso fazendo uma cópia exata de nossa matriz para que esta possa ser transformada enquanto a inicial permanece intacta.

Maneiras de copiar uma matriz:

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);

Tenha cuidado quando matrizes ou objetos estiverem aninhados !:

Quando matrizes são aninhadas, os valores são copiados por referência. Aqui está um exemplo de como isso pode levar a problemas:

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

Portanto, não use esses métodos quando houver objetos ou matrizes em sua matriz que você deseja copiar. ou seja, use esses métodos apenas em matrizes de primitivos.

Se você deseja fazer um deepclone de uma matriz javascript usada JSON.parseem conjunto com JSON.stringify, desta forma:

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

Desempenho da cópia:

Então, qual escolhemos para um desempenho ideal? Acontece que, o método mais detalhado, o forloop tem o melhor desempenho. Use o forloop para cópias realmente intensivas da CPU (grandes / muitas matrizes).

Depois disso, o .slice()método também possui um desempenho decente e também é menos detalhado e mais fácil para o programador implementar. Sugiro usar .slice()para a cópia diária de matrizes que não consomem muita CPU. Evite também usar JSON.parse(JSON.stringify(arr))(muita sobrecarga) se nenhum clone profundo for necessário e o desempenho for um problema.

Teste de desempenho da fonte

Willem van der Veen
fonte
3

Se sua matriz contiver elementos do tipo de dados primitivo , como int, char ou string, etc. , você poderá usar um desses métodos que retornará uma cópia da matriz original, como operador .slice () ou .map () ou spread ( graças ao ES6).

new_array = old_array.slice()

ou

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

ou

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

MAS, se a sua matriz contiver elementos complexos , como objetos (ou matrizes) ou mais objetos aninhados , você deverá certificar-se de que está fazendo uma cópia de todos os elementos do nível superior ao último nível, além da referência interna objetos serão usados ​​e isso significa que alterar valores em object_elements em new_array ainda afetará o old_array. Você pode chamar esse método de cópia em cada nível como fazer uma CÓPIA PROFUNDA da matriz_ antiga.

Para cópias profundas, você pode usar os métodos acima mencionados para tipos de dados primitivos em cada nível, dependendo do tipo de dados, ou pode usar esse método caro (mencionado abaixo) para fazer uma cópia profunda sem fazer muito trabalho.

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

Existem muitos outros métodos por aí que você pode usar dependendo de seus requisitos. Mencionei apenas alguns deles por fornecer uma idéia geral do que acontece quando tentamos copiar um array para outro por valor .

abhinav1602
fonte
Muito obrigado, a sua resposta foi o único que trabalhou para o meu cenário,
albert sh
2

Se você deseja fazer uma nova cópia de um objeto ou matriz, copie explicitamente as propriedades do objeto ou dos elementos da matriz, por exemplo:

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

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

Você pode procurar mais informações no Google sobre valores primitivos imutáveis ​​e referências a objetos mutáveis.

Sotiris
fonte
1
Você não precisa copiar explicitamente as propriedades dos objetos da matriz. Veja a resposta de Chtiwi Malek.
Magne
2

O uso da cópia profunda do jQuery pode ser feito da seguinte maneira:

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

Você também pode usar o operador de spread ES6 para copiar a matriz

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

Aqui estão mais algumas maneiras de copiar:

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
fonte
2

Exemplos rápidos:

  1. Se os elementos na matriz são tipos primitivos (sequência, número, 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. Se os elementos na matriz forem literais de objetos, outra matriz ({}, [])

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. Solução para 2 : cópia profunda por elemento por elemento

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
fonte
1

Aqui está uma 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
fonte
não é uma idéia tão ruim, embora seja melhor eu usar JSON stringify / analisar em vez de eval, e ainda assim uma outra comparação do jsPerf seria boa, verifique também que toSourcenão é padrão e não funcionará no Chrome, por exemplo.
dmi3y
1

Há o recém-introduzido Array.from, mas infelizmente, até o momento em que este artigo foi escrito, ele era suportado apenas nas versões recentes do Firefox (32 e superior). Pode ser simplesmente usado da seguinte maneira:

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

Referência: Aqui

Ou Array.prototype.mappode ser usado com uma função de identidade:

function identity(param)
{
    return param;
}

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

Referência: Aqui

Ashraf Sabry
fonte
+1 para mencionar Array.from, que agora é suportado em todos os principais navegadores, exceto no Internet Explorer ( origem ). .
mgthomas99
Esteja ciente de Array.fromque os dados serão alterados, não fornece cópia / clonagem profunda.
Sudhansu Choudhary
1

Para matriz ES6 contendo objetos

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