Skopiuj tablicę według wartości


1744

Podczas kopiowania tablicy w JavaScript do innej tablicy:

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

Uświadomiłem sobie, że arr2odnosi się to do tej samej tablicy arr1, a nie do nowej, niezależnej tablicy. Jak mogę skopiować tablicę, aby uzyskać dwie niezależne tablice?


3
Wygląda na to, że obecnie w Chrome 53 i Firefox 48 mamy świetną wydajność slicei spliceoperacje oraz nowego operatora rozprzestrzeniania i Array.frommamy znacznie wolniejszą implementację. Spójrz na perfjs.fnfo
Pencroff,

jsben.ch/#/wQ9RU <= ten test porównawczy przedstawia przegląd różnych sposobów kopiowania tablicy
EscapeNetscape


Na tej tablicy (taki, który zawiera pierwotne struny) można użyć var arr2 = arr1.splice();do głębokiej kopii, ale technika ta nie będzie działać, jeśli elementy macierzy zawierają struktury dosłownych (tj []lub {}) lub obiektów prototypowych (tj function () {}, newitp). Zobacz moje odpowiedzi poniżej, aby uzyskać dalsze rozwiązania.
tfmontague

16
Jest rok 2017, więc możesz rozważyć użycie funkcji ES6: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich

Odpowiedzi:


2700

Użyj tego:

var newArray = oldArray.slice();

Zasadniczo slice()operacja klonuje tablicę i zwraca odwołanie do nowej tablicy.

Pamiętaj również, że:

W przypadku odniesień, ciągów i liczb (a nie rzeczywistego obiektu) slice()kopiuje odniesienia do obiektu do nowej tablicy. Zarówno oryginalna, jak i nowa tablica odnoszą się do tego samego obiektu. Jeśli obiekt, do którego istnieje odwołanie, ulega zmianie, zmiany są widoczne zarówno dla nowych, jak i oryginalnych tablic.

Prymitywy takie jak łańcuchy i liczby są niezmienne, więc zmiany w łańcuchu lub liczbie są niemożliwe.


9
Jeśli chodzi o wydajność, poniższe testy jsPerf faktycznie pokazują, że var arr2 = arr1.slice () jest tak samo szybki jak var arr2 = arr1.concat (); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 i jsperf.com/copy-simple-array . Wynik jsperf.com/array-copy/5 zaskoczył mnie do tego stopnia, że ​​zastanawiam się, czy kod testowy jest prawidłowy.
Cohen,

94
Mimo że otrzymało to już mnóstwo pozytywnych opinii, zasługuje na inną, ponieważ poprawnie opisuje referencje w JS, co jest niestety dość rzadkie.
Wayne,

34
@ GáborImre dodajesz całą bibliotekę tylko dla czytelności? Naprawdę? Dodałbym tylko komentarz, gdybym martwił się o czytelność. Zobacz: var newArray = oldArray.slice (); // Sklonuj oldArray do newArray
dudewad

12
@ GáborImre Rozumiem, jasne. Ale rozwiązanie konkretnego problemu inżynieryjnego poprzez włączenie całej biblioteki nie jest moim zdaniem pomocne, jest to rozdęcie projektu. Widzę, że programiści często to robią, a potem powstaje projekt obejmujący całą platformę, która zastępuje konieczność pisania pojedynczej funkcji. Ale tylko moje MO.
dudewad

5
Wyciągnięta lekcja: Nie myl .slice()z tym .splice(), co daje pustą tablicę. Duża różnica.
crazypeter

530

W JavaScript, techniki głębokiego kopiowania zależą od elementów w tablicy. Zacznijmy od tego.

Trzy rodzaje elementów

Elementami mogą być: wartości dosłowne, struktury dosłowne lub prototypy.

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

Z tych elementów możemy stworzyć trzy typy tablic.

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

Techniki głębokiego kopiowania zależą od trzech typów tablic

W oparciu o typy elementów w tablicy, możemy użyć różnych technik do głębokiego kopiowania.

Techniki głębokiego kopiowania JavaScript według typów elementów

  • Tablica dosłownych wartościach (type1) , , , i techniki mogą być wykorzystywane do głębokich kopiowanie tablic z wartościami dosłownych (logiczna, numer i napis) tylko; gdzie operator Spread ma najlepszą wydajność ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).
    [...myArray]myArray.splice(0)myArray.slice()myArray.concat()[...myArray]

  • Tablica dosłownych wartościach (type1) i dosłownych strukturach (TYPE2) technika może być użyta do głębokiego kopiowanie wartości dosłownych (Boolean, liczba, ciąg) i struktur dosłownych (array, obiekt), ale nie prototyp obiektów.
    JSON.parse(JSON.stringify(myArray))

  • Wszystkie tablice (typ 1, typ 2, typ 3) Techniki
    jQuery $.extend(myArray)można używać do głębokiego kopiowania wszystkich typów tablic. Biblioteki takie jak Underscore i Lo-dash oferują podobne funkcje głębokiego kopiowania do jQuery $.extend() , ale mają niższą wydajność. Co bardziej zaskakujące, $.extend()ma wyższą wydajność niż JSON.parse(JSON.stringify(myArray))technika http://jsperf.com/js-deep-copy/15 .
    A dla programistów, którzy unikają bibliotek stron trzecich (takich jak jQuery), możesz użyć następującej funkcji niestandardowej; który ma wyższą wydajność niż $ .extend i kopiuje głęboko wszystkie tablice.

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

Aby odpowiedzieć na pytanie ...

Pytanie

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

Uświadomiłem sobie, że arr2 odnosi się do tej samej tablicy co arr1, a nie do nowej, niezależnej tablicy. Jak mogę skopiować tablicę, aby uzyskać dwie niezależne tablice?

Odpowiedź

Ponieważ arr1jest to tablica wartości literalnych (wartość logiczna, liczba lub łańcuch), możesz użyć dowolnej techniki głębokiego kopiowania omówionej powyżej, w której operator rozkładania ...ma najwyższą wydajność.

// 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

1
Wiele z tych podejść nie działa dobrze. Użycie operatora przypisania oznacza, że ​​musisz ponownie przypisać pierwotną wartość literału arr1. To bardzo rzadkie, że tak będzie. Używanie spliceobliteratów arr1, więc to wcale nie jest kopia. Użycie JSONzakończy się niepowodzeniem, jeśli którakolwiek z wartości w tablicy to Funkcje lub prototypy (takie jak a Date).
Dancrumb

Używanie splotu jest częściowym rozwiązaniem. Nie powiedzie się w znacznie większej liczbie przypadków niż JSON. Splice tworzy głęboką kopię ciągów i liczb, gdy przenosi wartości - nigdy nie powiedział, że zwraca kopię.
tfmontague

1
Dlaczego splice (0)? Czy nie powinno to być slice ()? Myślę, że to nie powinno modyfikować oryginalnej tablicy, co robi splice. @JamesMontagne
pomóż

2
splot utworzy wskaźniki do elementów w oryginalnej tablicy (płytka kopia). splice (0) przydzieli nową pamięć (głęboką kopię) dla elementów w tablicy, które są liczbami lub łańcuchami, i utworzy wskaźniki dla wszystkich innych typów elementów (płytka kopia). Przekazywanie wartości początkowej zera do metody funkcji łączenia nie powoduje łączenia żadnych elementów z oryginalnej tablicy, a zatem jej nie modyfikuje.
tfmontague

1
W rzeczywistości istnieje tylko jeden typ tablicy: tablica „czegoś”. Nie ma różnicy między [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]żadną inną tablicą.
wizzwizz4,

189

Za pomocą rozkładówek ...można kopiować tablice.

const itemsCopy = [...items];

Również jeśli chcesz utworzyć nową tablicę, której częścią jest już istniejąca:

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

Rozszerzenia tablic są teraz obsługiwane we wszystkich głównych przeglądarkach, ale jeśli potrzebujesz starszej obsługi, użyj maszynopisu lub babel i skompiluj do ES5.

Więcej informacji o spreadach


1
To nie zadziała w przypadku głębokiego kopiowania. Deep Clone an Array w JavaScript .
SNag

151

JQuery nie jest potrzebne ... Przykład roboczy

var arr2 = arr1.slice()

Spowoduje to skopiowanie tablicy od pozycji początkowej 0do końca tablicy.

Ważne jest, aby pamiętać, że będzie działać zgodnie z oczekiwaniami dla typów pierwotnych (ciąg, liczba itp.), A także wyjaśnić oczekiwane zachowanie dla typów odniesienia ...

Jeśli masz tablicę typów odwołań, powiedz typu Object. Tablica zostanie skopiowana, ale obie tablice będą zawierać odniesienia do tych samych Object. W takim przypadku wydaje się, że tablica jest kopiowana przez odniesienie, nawet jeśli tablica jest faktycznie kopiowana.


12
Nie, to nie byłaby głęboka kopia.
jondavidjohn

Spróbuj tego; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
Jaka jest różnica między tą odpowiedzią a odpowiedzią zaakceptowaną?
Isaac Pak

pojawia się błąd w konsoli dla podanego przykładu „TypeError: window.addEvent nie jest funkcją”
Ravi Sharma

72

Alternatywą slicejest concat, która może być używana na 2 sposoby. Pierwszy z nich jest być może bardziej czytelny, ponieważ zamierzone zachowanie jest bardzo jasne:

var array2 = [].concat(array1);

Druga metoda to:

var array2 = array1.concat();

Cohen (w komentarzach) zauważył, że ta ostatnia metoda ma lepszą wydajność .

Działa to tak, że concatmetoda tworzy nową tablicę składającą się z elementów w obiekcie, do którego jest wywoływana, a następnie elementów dowolnych tablic przekazywanych do niej jako argumenty. Więc gdy nie zostaną przekazane żadne argumenty, po prostu kopiuje tablicę.

Lee Penkman, również w komentarzach, zwraca uwagę, że jeśli istnieje szansa array1jest undefined, można powrócić pustą tablicę, co następuje:

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

Lub, dla drugiej metody:

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

Należy pamiętać, że można to zrobić także z slice: var array2 = (array1 || []).slice();.


31
Właściwie możesz także: var array2 = array1.concat (); Pod względem wydajności jest znacznie szybszy. (JSPerf: jsperf.com/copy-simple-array and jsperf.com/copy-array-slice-vs-concat/5
Cohen,

5
Warto zauważyć, że jeśli tablica1 nie jest tablicą, to [].concat(array1)zwraca [array1]np. Jeśli otrzymasz jej niezdefiniowaną wartość [undefined]. Czasami tak robięvar array2 = [].concat(array1 || []);
Lee Penkman

60

Tak to zrobiłem po wypróbowaniu wielu podejść:

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

Spowoduje to utworzenie nowej głębokiej kopii niezwiązanej z pierwszą (nie płytkiej kopii).

To oczywiście nie sklonuje zdarzeń i funkcji, ale dobrą rzeczą jest to, że możesz to zrobić w jednym wierszu i można go użyć do dowolnego rodzaju obiektu (tablic, ciągów, liczb, obiektów ...)


4
To jest najlepsze. Używam tej samej metody dawno temu i myślę, że nie ma sensu w starych pętlach rekurencyjnych
Vladimir Kharlampidi

1
Należy pamiętać, że ta opcja nie obsługuje dobrze struktur podobnych do wykresów: ulega awarii w obecności cykli i nie zachowuje wspólnych odniesień.
Ruben

1
Nie udaje się to również w przypadku rzeczy takich jak Dateprototyp. Ponadto undefineds są konwertowane na nulls.
Dancrumb

7
Czy nikt nie jest wystarczająco odważny, aby skomentować rażącą nieefektywność zarówno procesora, jak i pamięci w przypadku serializacji do tekstu, a następnie analizowania z powrotem do obiektu?
Lawrence Dol

3
To jedyne rozwiązanie, które zadziałało. Korzystanie z slice () jest naprawdę fałszywym rozwiązaniem.

20

Niektóre z wymienionych metod działają dobrze podczas pracy z prostymi typami danych, takimi jak liczba lub łańcuch, ale gdy tablica zawiera inne obiekty, metody te zawodzą. Kiedy próbujemy przekazać dowolny obiekt z jednej tablicy do drugiej, jest on przekazywany jako odniesienie, a nie obiekt.

Dodaj następujący kod do pliku 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;
};

I po prostu użyj

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

To będzie działać.


2
pojawia się ten błąd, gdy dodam ten kod do mojej strony „
Nieprzechwycony zakres błędu

1
Przepraszam, ten błąd występuje w chrome, jeśli arr1 nie jest zadeklarowany. więc skopiowałem i wkleiłem powyższy kod i otrzymuję błąd, jednak jeśli zadeklaruję tablicę arr1, to nie dostaję błędu. Możesz poprawić odpowiedź, deklarując arr1 tuż nad arr2. Widzę, że jest wielu „nas”, którzy nie zauważyli, że musimy zadeklarować arr1 (częściowo dlatego, że kiedy analizowałem twoją odpowiedź, spieszyłem się i potrzebowałem czegoś, co „po prostu działa”)
zobaczyłem

.slice()nadal działa dobrze, nawet jeśli masz obiekty w tablicy: jsfiddle.net/edelman/k525g
Jason

7
@Jason, ale obiekty nadal wskazują ten sam obiekt, więc zmiana jednego spowoduje zmianę drugiego. jsfiddle.net/k525g/1
Samuel

Doskonały kod. Mam jedno pytanie, tak naprawdę próbowałem skopiować jedną tablicę do drugiej, taką jak var arr1 = new Array (), a następnie var arr2 = arr1; Jeśli coś zmienię w arr2, zmiana dotyczy również arr1. Jeśli jednak użyję utworzonego przez Ciebie prototypu klonu, faktycznie tworzy on kompletną nową instancję tej tablicy lub innymi słowy, kopiuje ją. Czy to jest problem z przeglądarką? czy javascript domyślnie ustawia dwie zmienne, wskazując jedną na drugą za pomocą wskaźników, gdy ktoś zmienia var arr2 = arr1 i dlaczego nie dzieje się tak w przypadku zmiennych całkowitych? patrz jsfiddle.net/themhz/HbhtA
themhz


17

Osobiście uważam, że Array.from jest bardziej czytelnym rozwiązaniem. Nawiasem mówiąc, uważaj tylko na obsługę przeglądarki.

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

1
Tak, jest to bardzo czytelne. .slice()Rozwiązanie jest całkowicie unintuitive. Dzięki za to.
Banago

15

Ważny!

Większość odpowiedzi tutaj działa w określonych przypadkach .

Jeśli nie interesują Cię głębokie / zagnieżdżone obiekty i rekwizyty użyj ( ES6 ):

let clonedArray = [...array]

ale jeśli chcesz wykonać głęboki klon, użyj tego:

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


Dla użytkowników lodash:

let clonedArray = _.clone(array) dokumentacja

i

let clonedArray = _.cloneDeep(array) dokumentacja



9

Dodanie do rozwiązania array.slice (); pamiętaj, że jeśli masz wielowymiarową tablicę, tablice podrzędne zostaną skopiowane przez referencje. To, co możesz zrobić, to zapętlić i pokroić () każdą pod-macierz osobno

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

te same rzeczy dotyczą tablicy obiektów, zostaną one skopiowane przez odniesienie, musisz je skopiować ręcznie


Ta odpowiedź zasługuje na miejsce u góry strony! Pracowałem z wielowymiarowymi tablicami podrzędnymi i nie mogłem zrozumieć, dlaczego wewnętrzne tablice były zawsze kopiowane przez ref, a nie przez val. Ta prosta logika rozwiązała mój problem. W miarę możliwości dałbym ci +100!
Mac

8

Wartości pierwotne są zawsze przekazywane przez jego wartość (kopiowane). Wartości złożone są jednak przekazywane przez odniesienie.

Jak więc skopiować ten arr?

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

Skopiuj tablicę w ES6

let arrCopy = [...arr]; 

Skopiuj n Tablicę w ES5

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

Dlaczego `let arrCopy = arr` nie przekazuje wartości?

Przekazywanie jednej zmiennej do drugiej w przypadku wartości złożonych, takich jak Object / Array, zachowuje się inaczej. Używając operatora asign na wartościach copand przekazujemy odwołanie do obiektu. Dlatego wartość obu tablic zmienia się podczas usuwania / dodawania elementów tablic.

Wyjątki:

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

Gdy przypisujesz nową wartość do zmiennej, zmieniasz samą referencję i nie wpływa ona na oryginalny obiekt / tablicę.

Czytaj więcej


6

Jak wiemy w tablicach i obiektach JavaScript są referencyjne, ale w jaki sposób możemy skopiować tablicę bez zmiany oryginalnej tablicy później?

Oto kilka sposobów, aby to zrobić:

Wyobraź sobie, że w kodzie mamy tę tablicę:

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

1) Zapętlanie tablicy przez funkcję i zwracanie nowej tablicy, jak poniżej:

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

2) Przy użyciu metody wycinania plasterek służy do wycinania części tablicy, wycina część tablicy bez dotykania oryginału, w wycinku, jeśli nie określi się początku i końca tablicy, wycina całość tablicę i w zasadzie wykonaj pełną kopię tablicy, dzięki czemu możemy łatwo powiedzieć:

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

3) Również metoda kontaktowa, służy do scalenia dwóch tablic, ale możemy po prostu określić jedną z tablic, a następnie w zasadzie wykonać kopię wartości w nowej tablicy kontaktowej:

var arr2 = arr.concat();

4) Metoda strunizacji i parsowania, nie jest zalecana, ale może być łatwym sposobem kopiowania tablicy i obiektów:

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

5) Metoda Array.from, nie jest to powszechnie obsługiwane, przed użyciem sprawdź obsługę w różnych przeglądarkach:

const arr2 = Array.from(arr);

6) Sposób ECMA6, również nie w pełni obsługiwany, ale babelJs może ci pomóc, jeśli chcesz dokonać transpozycji:

const arr2 = [...arr];

6
let a = [1,2,3];

Teraz możesz wykonać dowolną z poniższych czynności, aby utworzyć kopię tablicy.

let b = Array.from(a); 

LUB

let b = [...a];

LUB

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

LUB

let b = a.slice(); 

LUB

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

Teraz, jeśli zmienię

a.push(5); 

Zatem a wynosi [1,2,3,5], ale b jest nadal [1,2,3], ponieważ ma inne odniesienie.

Ale myślę, że we wszystkich powyższych metodach Array.from jest lepszy i stworzony głównie do kopiowania tablicy.


1
który jest najszybszy?
Marc Frame


4

W moim szczególnym przypadku musiałem upewnić się, że tablica pozostała nienaruszona, więc zadziałało to dla mnie:

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

2
+1 za brak dodawania nieprzejrzystości do twojego kodu poprzez wywołanie funkcji, która robi dokładnie to samo, ale w mniej oczywisty sposób. slice może być bardziej wydajny pod maską, ale dla każdego, kto pracuje nad kodem, pokazuje to twoją intencję. a ponadto ułatwia późniejszą optymalizację, jeśli chcesz (na przykład) odfiltrować to, co kopiujesz. należy jednak pamiętać, że nie obsługuje to głębokiego kopiowania, a te same obiekty wewnętrzne są przekazywane do nowej tablicy przez odniesienie. To może być to, co chcesz zrobić, a może nie.
niezsynchronizowany

4

Utwórz kopię wielowymiarowej tablicy / obiektu:

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

Dzięki James Padolsey za tę funkcję.

Źródło: tutaj


3

Dan, nie musisz używać fantazyjnych sztuczek. Wystarczy, że zrobisz kopię arr1.

var arr2 = new Array(arr1);

Teraz arr1i arr2dwa różne zmienne przechowywanym w oddzielnych stosach. Sprawdź to na jsfiddle .


To nie kopiuje tablicy. Tworzy tablicę z jednym elementem, który odwołuje się do oryginału (tj var arr2 = [arr1];.).
Timothy003

3

Kiedy chcemy skopiować tablicę za pomocą operatora przypisania ( =), nie tworzy ona kopii, a jedynie kopiuje wskaźnik / odwołanie do tablicy. Na przykład:

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

Często podczas przekształcania danych chcemy zachować nienaruszoną naszą początkową strukturę danych (np. Macierz). Robimy to, robiąc dokładną kopię naszej tablicy, aby ta mogła zostać przekształcona, podczas gdy początkowa pozostaje nienaruszona.

Sposoby kopiowania tablicy:

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

Zachowaj ostrożność, gdy tablice lub obiekty są zagnieżdżone !:

Po zagnieżdżeniu tablic wartości są kopiowane przez odniesienie. Oto przykład, w jaki sposób może to prowadzić do problemów:

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

Dlatego nie używaj tych metod, gdy w tablicy znajdują się obiekty lub tablice, które chcesz skopiować. tzn. używaj tych metod tylko do tablic prymitywów.

Jeśli chcesz głęboko sklonować użycie tablicy javascript JSON.parsew połączeniu z JSON.stringify:

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

Wykonanie kopiowania:

Który wybieramy dla uzyskania optymalnej wydajności. Okazuje się, że najbardziej szczegółowa metoda, forpętla ma najwyższą wydajność. Użyj forpętli do naprawdę intensywnego kopiowania procesora (duże / wiele tablic).

Następnie .slice()metoda ma również przyzwoitą wydajność, jest również mniej szczegółowa i łatwiejsza do wdrożenia przez programistę. Proponuję używać .slice()do codziennego kopiowania tablic, które nie wymagają dużego obciążenia procesora. Unikaj także korzystania z JSON.parse(JSON.stringify(arr))(dużego obciążenia), jeśli nie jest wymagany głęboki klon, a wydajność stanowi problem.

Test wydajności źródła


3

Jeśli tablica zawiera elementy pierwotnego typu danych, takie jak int, char lub string itp., Możesz użyć jednej z tych metod, która zwraca kopię oryginalnej tablicy, takiej jak .slice () lub .map () lub operator rozkładania ( dzięki ES6).

new_array = old_array.slice()

lub

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

lub

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

ALE jeśli twoja tablica zawiera złożone elementy, takie jak obiekty (lub tablice) lub więcej zagnieżdżonych obiektów , musisz upewnić się, że wykonujesz kopię wszystkich elementów z najwyższego poziomu na ostatni poziom odniesienia do wewnętrznej obiekty będą używane, co oznacza, że ​​zmiana wartości w elementach_obiektu w nowej_tablicy będzie nadal wpływać na starą_tablicę. Możesz nazwać tę metodę kopiowania na każdym poziomie jako GŁĘBOKIE KOPIOWANIE GŁĘBOKIE starej tablicy.

Do głębokiego kopiowania można użyć wyżej wymienionych metod dla prymitywnych typów danych na każdym poziomie w zależności od rodzaju danych lub można użyć tej kosztownej metody (wspomnianej poniżej) do wykonania głębokiej kopii bez większego nakładu pracy.

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

Istnieje wiele innych metod, których można użyć w zależności od wymagań. Wspomniałem tylko o niektórych z nich, aby dać ogólny obraz tego, co się dzieje, gdy próbujemy skopiować tablicę na drugą pod względem wartości .


Wielkie dzięki, twoja odpowiedź była jedyną, która pracowała dla mojego scenariusza,
albert sh

2

Jeśli chcesz utworzyć nową kopię obiektu lub tablicy, musisz jawnie skopiować właściwości obiektu lub elementów tablicy, na przykład:

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

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

Możesz wyszukiwać w Google więcej informacji o niezmiennych pierwotnych wartościach i odwoływalnych obiektach.


1
Nie musisz jawnie kopiować właściwości obiektów tablicy. Zobacz odpowiedź Chtiwi Malek.
Magne

2

Za pomocą głębokiej kopii jQuery można wykonać w następujący sposób:

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

2

Możesz także użyć operatora rozkładania ES6 do skopiowania macierzy

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

2

Oto kilka innych sposobów kopiowania:

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

Szybkie przykłady:

  1. Jeśli elementy w tablicy są typami pierwotnymi (ciąg, liczba itp.)

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. Jeśli elementy w tablicy są literałami obiektów, inna tablica ({}, [])

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. Rozwiązanie dla 2 : Głębokie kopiowanie element po elemencie

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


1

Oto wariant:

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

nie taki zły pomysł, chociaż lepiej użyć JSON stringify / parsować zamiast eval, a jeszcze inne porównanie jsPerf byłoby dobre do sprawdzenia, również uwaga toSourcenie jest standardowa i na przykład nie będzie działać w Chrome.
dmi3y

1

Jest nowo wprowadzony Array.from, ale niestety, w chwili pisania tego tekstu jest obsługiwany tylko w najnowszych wersjach Firefoksa (32 i nowszych). Można go po prostu użyć w następujący sposób:

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

Referencja: tutaj

Lub Array.prototype.mapmoże być używany z funkcją tożsamości:

function identity(param)
{
    return param;
}

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

Referencja: tutaj


+1 za wzmiankę Array.from, która jest teraz obsługiwana we wszystkich głównych przeglądarkach oprócz Internet Explorera ( źródło ). .
mgthomas99

Pamiętaj, Array.fromże mutuje dane, nie zapewnia głębokiego kopiowania / głębokiego klonowania.
Sudhansu Choudhary

1

Dla tablicy zawierającej obiekty ES6

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.