web-dev-qa-db-de.com

Tiefe Kopie in ES6 mit dem Spread-Zeichen

Ich versuche, eine Deep Copy Map-Methode für mein Redux-Projekt zu erstellen, die mit Objekten und nicht mit Arrays arbeitet. Ich habe gelesen, dass in Redux jeder Zustand in den vorherigen Zuständen nichts ändern sollte.

export const mapCopy = (object, callback) => {
    return Object.keys(object).reduce(function (output, key) {

    output[key] = callback.call(this, {...object[key]});

    return output;
    }, {});
}

Es klappt:

    return mapCopy(state, e => {

            if (e.id === action.id) {
                 e.title = 'new item';
            }

            return e;
        })

Es werden jedoch keine inneren Elemente tief kopiert, daher muss ich es anpassen:

export const mapCopy = (object, callback) => {
    return Object.keys(object).reduce(function (output, key) {

    let newObject = {...object[key]};
    newObject.style = {...newObject.style};
    newObject.data = {...newObject.data};

    output[key] = callback.call(this, newObject);

    return output;
    }, {});
}

Dies ist weniger elegant, da es erforderlich ist zu wissen, welche Objekte übergeben werden. Gibt es eine Möglichkeit in ES6, das Spread-Zeichen zu verwenden, um ein Objekt tief zu kopieren?

53
Guy

Es ist keine solche Funktionalität in ES6 integriert. Ich denke, Sie haben einige Optionen, je nachdem, was Sie tun möchten.

Wenn Sie wirklich tiefes Kopieren möchten:

  1. Verwenden Sie eine Bibliothek. Beispielsweise hat lodash die Methode cloneDeep .
  2. Implementieren Sie Ihre eigene Klonfunktion.

Alternative Lösung für Ihr spezifisches Problem (keine tiefe Kopie)

Ich denke jedoch, wenn Sie bereit sind, ein paar Dinge zu ändern, können Sie sich etwas Arbeit sparen. Ich gehe davon aus, dass Sie alle Anrufseiten für Ihre Funktion steuern.

  1. Geben Sie an, dass alle an mapCopy übergebenen Rückrufe neue Objekte zurückgeben müssen, anstatt das vorhandene Objekt zu mutieren. Zum Beispiel:

    mapCopy(state, e => {
      if (e.id === action.id) {
        return Object.assign({}, e, {
          title: 'new item'
        });
      } else {  
        return e;
      }
    });
    

    Dies verwendet Object.assign , um ein neues Objekt zu erstellen, legt die Eigenschaften von e für dieses neue Objekt fest und setzt dann einen neuen Titel für das neue Objekt. Das bedeutet, dass Sie niemals vorhandene Objekte mutieren und nur bei Bedarf neue Objekte erstellen. 

  2. mapCopy kann jetzt ganz einfach sein:

    export const mapCopy = (object, callback) => {
      return Object.keys(object).reduce(function (output, key) {
        output[key] = callback.call(this, object[key]);
        return output;
      }, {});
    }
    

Im Wesentlichen vertraut mapCopy darauf, dass die Anrufer das Richtige tun. Deshalb habe ich gesagt, dies setzt voraus, dass Sie alle Anrufseiten kontrollieren.

48
Frank Tan

Verwenden Sie diese Option stattdessen für tiefes Kopieren

var newObject = JSON.parse(JSON.stringify(oldObject))

var oldObject = {
  name: 'A',
  address: {
    street: 'Station Road',
    city: 'Pune'
  }
}
var newObject = JSON.parse(JSON.stringify(oldObject));

newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);

60
Nikhil Mahirrao

Von MDN

Hinweis: Die Spread-Syntax geht beim Kopieren eines Arrays effektiv eine Ebene tiefer. Daher ist es möglicherweise für das Kopieren mehrdimensionaler Arrays ungeeignet, wie das folgende Beispiel zeigt (dies ist mit Object.assign () und der Spread-Syntax identisch).

Ich persönlich empfehle die Verwendung der Lodash-Funktion cloneDeep für das Klonen von Objekten/Arrays auf mehreren Ebenen.

Hier ist ein Arbeitsbeispiel:

const arr1 = [{ 'a': 1 }];

const arr2 = [...arr1];

const arr3 = _.clone(arr1);

const arr4 = arr1.slice();

const arr5 = _.cloneDeep(arr1);

const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!


// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false

// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

18
Mina Luke

Ich benutze oft das:

function deepCopy(obj) {
    if(typeof obj !== 'object' || obj === null) {
        return obj;
    }

    if(obj instanceof Date) {
        return new Date(obj.getTime());
    }

    if(obj instanceof Array) {
        return obj.reduce((arr, item, i) => {
            arr[i] = deepCopy(item);
            return arr;
        }, []);
    }

    if(obj instanceof Object) {
        return Object.keys(obj).reduce((newObj, key) => {
            newObj[key] = deepCopy(obj[key]);
            return newObj;
        }, {})
    }
}
2
HectorGuo

Ich selbst bin letzten Tag auf diesen Antworten gelandet und habe versucht, einen Weg zu finden, komplexe Strukturen, einschließlich rekursiver Links, tief zu kopieren. Da ich mit den Vorschlägen bisher nicht zufrieden war, habe ich dieses Rad selbst umgesetzt. Und es funktioniert ganz gut. Hoffe es hilft jemandem.

Beispielverwendung:

OriginalStruct.deep_copy = deep_copy; // attach the function as a method

TheClone = OriginalStruct.deep_copy();

Bitte schauen Sie unter https://github.com/latitov/JS_DeepCopy nach, um Live-Beispiele für die Verwendung zu erhalten, und auch deep_print () ist verfügbar.

Wenn Sie es schnell brauchen, finden Sie hier die Quelle der deep_copy () -Funktion:

function deep_copy() {
    'use strict';   // required for undef test of 'this' below

    // Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated.

    var id_cnt = 1;
    var all_old_objects = {};
    var all_new_objects = {};
    var root_obj = this;

    if (root_obj === undefined) {
        console.log(`deep_copy() error: wrong call context`);
        return;
    }

    var new_obj = copy_obj(root_obj);

    for (var id in all_old_objects) {
        delete all_old_objects[id].__temp_id;
    }

    return new_obj;
    //

    function copy_obj(o) {
        var new_obj = {};
        if (o.__temp_id === undefined) {
            o.__temp_id = id_cnt;
            all_old_objects[id_cnt] = o;
            all_new_objects[id_cnt] = new_obj;
            id_cnt ++;

            for (var prop in o) {
                if (o[prop] instanceof Array) {
                    new_obj[prop] = copy_array(o[prop]);
                }
                else if (o[prop] instanceof Object) {
                    new_obj[prop] = copy_obj(o[prop]);
                }
                else if (prop === '__temp_id') {
                    continue;
                }
                else {
                    new_obj[prop] = o[prop];
                }
            }
        }
        else {
            new_obj = all_new_objects[o.__temp_id];
        }
        return new_obj;
    }
    function copy_array(a) {
        var new_array = [];
        if (a.__temp_id === undefined) {
            a.__temp_id = id_cnt;
            all_old_objects[id_cnt] = a;
            all_new_objects[id_cnt] = new_array;
            id_cnt ++;

            a.forEach((v,i) => {
                if (v instanceof Array) {
                    new_array[i] = copy_array(v);
                }
                else if (v instanceof Object) {
                    new_array[i] = copy_object(v);
                }
                else {
                    new_array[i] = v;
                }
            });
        }
        else {
            new_array = all_new_objects[a.__temp_id];
        }
        return new_array;
    }
}

[email protected]!

1
Leonid Titov
function deepclone(obj) {
    let newObj = {};

    if (typeof obj === 'object') {
        for (let key in obj) {
            let property = obj[key],
                type = typeof property;
            switch (type) {
                case 'object':
                    if( Object.prototype.toString.call( property ) === '[object Array]' ) {
                        newObj[key] = [];
                        for (let item of property) {
                            newObj[key].Push(this.deepclone(item))
                        }
                    } else {
                        newObj[key] = deepclone(property);
                    }
                    break;
                default:
                    newObj[key] = property;
                    break;

            }
        }
        return newObj
    } else {
        return obj;
    }
}
1
Jeroen Breen
// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
  // return non object values
  if('object' !==typeof o) return o
  // m: a map of old refs to new object refs to stop recursion
  if('object' !==typeof m || null ===m) m =new WeakMap()
  var n =m.get(o)
  if('undefined' !==typeof n) return n
  // shallow/leaf clone object
  var c =Object.getPrototypeOf(o).constructor
  // TODO: specialize copies for expected built in types i.e. Date etc
  switch(c) {
    // shouldn't be copied, keep reference
    case Boolean:
    case Error:
    case Function:
    case Number:
    case Promise:
    case String:
    case Symbol:
    case WeakMap:
    case WeakSet:
      n =o
      break;
    // array like/collection objects
    case Array:
      m.set(o, n =o.slice(0))
      // recursive copy for child objects
      n.forEach(function(v,i){
        if('object' ===typeof v) n[i] =clone(v, m)
      });
      break;
    case ArrayBuffer:
      m.set(o, n =o.slice(0))
      break;
    case DataView:
      m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
      break;
    case Map:
    case Set:
      m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
      break;
    case Int8Array:
    case Uint8Array:
    case Uint8ClampedArray:
    case Int16Array:
    case Uint16Array:
    case Int32Array:
    case Uint32Array:
    case Float32Array:
    case Float64Array:
      m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
      break;
    // use built in copy constructor
    case Date:
    case RegExp:
      m.set(o, n =new (c)(o))
      break;
    // fallback generic object copy
    default:
      m.set(o, n =Object.assign(new (c)(), o))
      // recursive copy for child objects
      for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m)
  }
  return n
}
0
user10919042
const cloneData = (dataArray) => {
    newData= []
    dataArray.forEach((value) => {
        newData.Push({...value})
    })
    return newData
}
  • a = [{name: "siva"}, {name: "siva1"}];
  • b = myCopy (a)
  • b === a // false`
0
Harish Sekar

Ich suchte nach einer Möglichkeit, ein Objekt funktional in JavaScript tief zu kopieren. Ich konnte keinen finden, also habe ich es geschafft. Dieser Algorithmus ist unveränderlich, erzeugt keine Nebenwirkungen und ist eher deklarativ als imperativ. Genießen!

const deepCopy = obj => (Array.isArray(obj) ? Object.values : obj=>obj)(Object.keys(obj).reduce((acc, key) => 
  ({...acc, [key]: (
    !obj[key] ? obj[key]
    : typeof obj[key] === 'object' ? deepCopy(obj[key])
    : obj[key]
  )}),
  {}
))

Im Wesentlichen geht es darum, dass die Schlüssel des Objekts zu einem anderen Objekt reduziert werden. Es behandelt rekursiv verschachtelte Objekte und Arrays und konvertiert Arrays in Objekte mit den numerischen Indizes, die vor dem Kopieren als Schlüssel verwendet werden. Um konvertierte verschachtelte Arrays wieder in Objekte einzugeben, verwende ich Object.values.

Die obige Funktion führt die folgende Abfolge von Operationen aus: 

  1. Wenn das zu kopierende Objekt ein Array ist, 1a) führe seinen aufgelösten Wert an Object.values ​​(), andernfalls 2b) setze die Funktion so, dass das aufgelöste Objekt zurückgegeben wird.
  2. Erstellen Sie ein Array aus den Schlüsseln des zu kopierenden Objekts.
  3. Führen Sie das Tastenfeld in die Reduzierfunktion ein.
  4. Setzen Sie den Wert für das neue Objekt unter dem angegebenen Schlüssel: it; 4c) Andernfalls setzen Sie es.
0
Jacob Penney