2012-12-07 8 views
29

Eventuali duplicati:
What is the most efficient way to clone a JavaScript object?creare una copia di array multi-dimensionale, non fare riferimento - JavaScript

Questo è indicato anche come "copia profonda", che ho trovato un po ' articoli su Il più vicino sembra essere this one ma è per jQuery - Sto provando a farlo senza una libreria.

Ho visto anche, in due posti, che è possibile fare qualcosa di simile:

arr2 = JSON.decode(JSON.encode(arr1)); 

Ma che apparentemente inefficiente. È anche possibile eseguire il ciclo e copiare ogni valore singolarmente e ricorre attraverso tutti gli array. Sembra stancante e inefficiente.

Quindi qual è il modo più efficiente e non librerie per copiare un array multidimensionale JavaScript [[a],[b],[c]]? Sono completamente soddisfatto di un metodo "non-IE", se necessario.

Grazie!

+0

Quanto è efficiente? Stai facendo questo più e più volte nel client (o è questo lato server come Nodo)? Il metodo JSON stringify -> parse è molto scorrevole anche se non è il più efficiente. –

+0

Altrimenti, la copia profonda significa loop ricorsivo ... –

+0

Quali tipi di dati la tua struttura terrà? Sono solo matrici o altri oggetti? È noto quanto sia profonda la tua struttura? –

risposta

38

Poiché sembra che tu abbia a che fare con un array di array ad un livello sconosciuto di profondità, ma devi solo gestirli ad un livello in profondità in un dato momento, quindi sarà semplice e veloce utilizzare .slice().

var newArray = []; 

for (var i = 0; i < currentArray.length; i++) 
    newArray[i] = currentArray[i].slice(); 

o utilizzando .map() al posto del ciclo for:

var newArray = currentArray.map(function(arr) { 
    return arr.slice(); 
}); 

Quindi questo itera l'Array corrente, e costruisce una nuova serie di copie poco profonde delle matrici nidificate. Poi quando andrai al livello successivo di profondità, faresti la stessa cosa.

Ovviamente, se è presente una combinazione di matrici e altri dati, è necessario testare ciò che è prima di tagliare.

+0

Immagino che la mappa con le sue chiamate di funzioni extra sia molto più lenta nella maggior parte dei browser, anche se potrebbe essere ottimizzata da inlining e quindi potrebbe funzionare meglio. – Bergi

+0

@Bergi: Sì, la versione della mappa avrà un piccolo successo, ma è carina e pulita. –

+0

Wow, la mappa è davvero pulita –

5

io non sono sicuro di quanto meglio JSON.stringy e JSON.parse di encode e decode, ma si potrebbe provare:

JSON.parse(JSON.stringify(array)); 

Un'altra cosa che ho trovato (anche se mi piacerebbe modifico un po '):

http://www.xenoveritas.org/blog/xeno/the-correct-way-to-clone-javascript-arrays

function deepCopy(obj) { 
    if (typeof obj == 'object') { 
    if (isArray(obj)) { 
     var l = obj.length; 
     var r = new Array(l); 
     for (var i = 0; i < l; i++) { 
     r[i] = deepCopy(obj[i]); 
     } 
     return r; 
    } else { 
     var r = {}; 
     r.prototype = obj.prototype; 
     for (var k in obj) { 
     r[k] = deepCopy(obj[k]); 
     } 
     return r; 
    } 
    } 
    return obj; 
} 
+0

Lo svantaggio di entrambi i metodi è che non gestiscono cicli auto-referenziali - qualcosa che può o non può essere importante (non sono sicuro di cosa farà la versione JSON, ma il secondo esempio verrà ripetuto all'infinito) – Jeff

+0

@Jeff Mi fido di te, ma posso dai un (piccolo) esempio? Volevo solo capire – Ian

+0

'var x = {}; xy = x; 'colpirà l'istruzione' else' ogni volta che viene chiamato 'deepCopy', e JSON semplicemente non può gestire i riferimenti personali - è innaturalmente basato sull'albero – Jeff

3

Qualsiasi algoritmo ricorsivo che non visitare lo stesso nodo due volte sarà di circa efficiente quanto si ottiene con javascript (almeno in un browser) - in certe situazioni in altre lingue si potrebbe farla franca con copie di memoria, ma javascript ovviamente non ha quell'abilità.

Suggerirei di trovare qualcuno che sia already done it e di utilizzare la loro implementazione per essere sicuri di averlo fatto bene - deve essere definito solo una volta.

4

Come richiesto per le prestazioni, suppongo che si andrebbe anche con una soluzione non generica. Per copiare un array multidimensionale con un numero noto di livelli, dovresti scegliere la soluzione più semplice, alcuni for-loop annidati.Per il vostro array a due dimensioanl, semplicemente sarebbe simile a questa:

var len = arr.length, 
    copy = new Array(len); // boost in Safari 
for (var i=0; i<len; ++i) 
    copy[i] = arr[i].slice(0); 

Il nativo slice method è più efficiente di un personalizzato per-loop, ma non crea copie di profondità, in modo che possiamo usarlo solo al livello più basso.