javascript - Kopier array etter verdi


Når du kopierer et array i JavaScript til et annet array:

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

Jeg innså at arr2 refererer til den samme arrayen som arr1, i stedet for en ny, uavhengig array. Hvordan kan jeg kopiere arrayen for å få to uavhengige arrayer?

Svar #1

Bruk dette:

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

let newArray = oldArray.slice();

console.log({newArray});

I utgangspunktet kloner slice()-operasjonen arrayen og returnerer en referanse til en ny array.

Merk også at:

For referanser, strenger og tall (og ikke selve objektet), slice() kopierer objektreferanser inn i det nye arrayet. Både det originale og det nye arrayet refererer til det samme objektet. Hvis et referert objekt endres, er endringene synlige for både det nye og det originale arrayet.

Primitiver som strenger og tall er uforanderlige, så endringer i strengen eller tallet er umulige.

Svar #2

I Javascript avhenger teknikker for dypkopiering av elementene i et array. La oss begynne der.

Tre typer elementer

Elementer kan være: bokstavelige verdier, bokstavelige strukturer eller prototyper.

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

Fra disse elementene kan vi lage tre typer matriser.

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

Dype kopieringsteknikker avhenger av de tre arraytypene

Basert på typene av elementer i arrayen, kan vi bruke ulike teknikker for å lage en dyp kopi.

Dypkopieringsteknikker

Benchmarker

https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison

  • Array av bokstavelige verdier (type1)
    Teknikkene [ ...myArray ], myArray.splice(0), myArray.slice() og myArray.concat() kan brukes for å lage en dyp kopi av arrays med kun bokstavelige verdier (boolean, tall og streng); hvor slice() har best ytelse i Chrome, og spread ... har best ytelse i Firefox.

  • Array av literal-verdier (type1) og literal-strukturer (type2)
    Teknikken JSON.parse(JSON.stringify(myArray)) kan brukes til å lage en dyp kopi av literal-verdier (boolean, tall, streng) og literal-strukturer (array, objekt), men ikke prototype-objekter.

  • Alle matriser (type1, type2, type3)

    • structuredClone();
    • Lo-dash cloneDeep(myArray) eller jQuery extend(true, [], myArray) teknikkene kan brukes til å dyptkopiere alle array-typer. Hvor Lodash cloneDeep() teknikken har høyest ytelse.
    • Og for de som unngår tredjepartsbiblioteker, vil den tilpassede funksjonen nedenfor lage en dyptkopi av alle array-typer, med lavere ytelse enn cloneDeep() og høyere ytelse enn extend(true).
function copy(aObject) {
  // Prevent undefined objects
  // if (!aObject) return aObject;

  let bObject = Array.isArray(aObject) ? [] : {};

  let value;
  for (const key in aObject) {

    // Prevent self-references to parent object
    // if (Object.is(aObject[key], aObject)) continue;
    
    value = aObject[key];

    bObject[key] = (typeof value === "object") ? copy(value) : value;
  }

  return bObject;
}

Så for å svare på spørsmålet...

Spørsmål

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

Jeg innså at arr2 refererer til det samme arrayet som arr1, i stedet for et nytt, uavhengig array. Hvordan kan jeg kopiere arrayet for å få to uavhengige arrays?

Svar

Fordi arr1 er et array av bokstavelige verdier (boolean, nummer eller streng), kan du bruke hvilken som helst teknikk for dyp kopi som er diskutert ovenfor, hvor slice() og spread ... har høyest ytelse.

arr2 = arr1.slice();
arr2 = [...arr1];
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = structuredClone(arr1);
arr2 = copy(arr1); // Custom function needed, and provided above
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = jQuery.extend(true, [], arr1); // jQuery.js needed

Svar #3

Du kan bruke array-spredninger ... for å kopiere arrays.

const itemsCopy = [...items];

Også hvis du vil lage et nytt array med det eksisterende som en del av det:

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

Array-spredninger støttes nå i alle store nettlesere, men hvis du trenger eldre støtte, bruk TypeScript eller Babel og kompiler til ES5.

Mer info om spreads

Svar #4

Ingen jQuery nødvendig... Fungerende eksempel

var arr2 = arr1.slice()

Dette kopierer arrayet fra startposisjonen 0 til slutten av arrayet.

Det er viktig å merke seg at det vil fungere som forventet for primitive typer (streng, tall, osv.), og å også forklare forventet oppførsel for referansetyper...

Hvis du har en matrise av referansetyper, si av typen Object. Matrisen vil bli kopiert, men begge matrisene vil inneholde referanser til de samme Objectene. Så i dette tilfellet vil det virke som om matrisen er kopiert ved referanse, selv om matrisen faktisk er kopiert.

Svar #5

Slik har jeg gjort det etter å ha prøvd mange tilnærminger:

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

Dette vil lage en ny dyp kopi som ikke er relatert til den første (ikke en grunn kopi).

Dette vil selvfølgelig heller ikke klone hendelser og funksjoner, men det gode er at du kan gjøre det på én linje, og det kan brukes for alle typer objekter (arrays, strenger, tall, objekter ...)

Svar #6

Et alternativ til slice er concat, som kan brukes på 2 måter. Den første av disse er kanskje mer lesbar ettersom den tiltenkte oppførselen er veldig klar:

var array2 = [].concat(array1);

Den andre metoden er:

var array2 = array1.concat();

Cohen (i kommentarene) påpekte at denne siste metoden har bedre ytelse.

Måten dette fungerer på er at concat-metoden oppretter et nytt array som består av elementene i objektet den kalles på, etterfulgt av elementene i eventuelle arrays som sendes til den som argumenter. Så når ingen argumenter sendes, kopierer den enkelt og greit arrayet.

Lee Penkman, også i kommentarene, påpeker at hvis det er en sjanse for at array1 er undefined, kan du returnere en tom array som følger:

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

Eller, for den andre metoden:

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

Merk at du også kan gjøre dette med slice: var array2=(array1 || []).slice();.

Svar #7

Viktig!

De fleste svar her fungerer for spesifikke tilfeller.

Hvis du ikke bryr deg om dypt/nestede objekter og egenskaper, bruk (ES6):

let clonedArray = [...array]

men hvis du vil gjøre en dyp kloning, bruk dette i stedet:

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

*funksjoner vil ikke bli bevart (serialisert) når du bruker stringify, du vil få resultatet uten dem.


For lodash-brukere:

let clonedArray=_.clone(array) dokumentasjon

og

let clonedArray=_.cloneDeep(array) dokumentasjon

Svar #8

Jeg personlig synes Array.from er en mer lesbar løsning. Forresten, vær bare oppmerksom på nettleserstøtten.

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

// deep clone
let clone = arr => Array.from(arr, item => Array.isArray(item) ? clone(item) : item);
x = [1, [], [[]]];
y = clone(x);
console.log({y});

Svar #9

Noen av de nevnte metodene fungerer godt når man arbeider med enkle datatyper som tall eller streng, men når arrayen inneholder andre objekter, feiler disse metodene. Når vi prøver å overføre et objekt fra en array til en annen, blir det overført som en referanse, ikke som selve objektet.

Legg til følgende kode i JavaScript-filen din:

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

Og bare bruk

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

Det vil fungere.

Svar #10

Fra ES2015,

var arr2 = [...arr1];

Svar #11

Hvis du befinner deg i et miljø med ECMAScript 6, kan du bruke Spread-operatoren på denne måten:

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>

Svar #12

Primitive verdier blir alltid sendt etter sin verdi (kopiert). Sammensatte verdier blir derimot sendt etter referanse.

Så hvordan kopierer vi denne arr?

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

Kopier et array i ES6

let arrCopy = [...arr]; 

Kopier n Array i ES5

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

Hvorfor `let arrCopy=arr` ikke passerer etter verdi?

Å sende en variabel til en annen på sammensatte verdier som objekt/array oppfører seg forskjellig. Ved bruk av tildelingsoperatoren på sammensatte verdier sender vi en referanse til et objekt. Dette er grunnen til at verdien av begge arrayene endres når vi fjerner/legger til elementer i arrayen.

Unntak:

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

Når du tildeler en ny verdi til variabelen, endrer du referansen selv, og det påvirker ikke det opprinnelige objektet/arrayet.

les mer

Relaterte artikler