Entfernen Sie den Wert vom Objekt ohne Mutation

96

Was ist eine gute und kurze Möglichkeit, einen Wert mit einem bestimmten Schlüssel aus einem Objekt zu entfernen, ohne das ursprüngliche Objekt zu mutieren?

Ich möchte etwas tun wie:

let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined

Ich weiß, dass es für solche Aufgaben viele Unveränderlichkeitsbibliotheken gibt, aber ich würde gerne ohne Bibliothek davonkommen. Um dies zu tun, wäre es jedoch erforderlich, einen einfachen und kurzen Weg zu haben, der im gesamten Code verwendet werden kann, ohne die Methode als Dienstprogrammfunktion zu abstrahieren.

Zum Beispiel mache ich Folgendes, um einen Wert hinzuzufügen:

let o2 = {...o1, age: 31};

Dies ist ziemlich kurz, leicht zu merken und benötigt keine Utility-Funktion.

Gibt es so etwas zum Entfernen eines Wertes? ES6 ist sehr willkommen.

Vielen Dank!

amann
quelle
"Einen Wert entfernen, ohne das Objekt zu mutieren" macht keinen Sinn. Sie können etwas nicht entfernen und gleichzeitig intakt halten. Was Sie tatsächlich tun, ist eine Teilkopie zu erstellen.
JJJ

Antworten:

212

Aktualisieren:

Sie können eine Eigenschaft mit einer schwierigen Destrukturierungszuweisung aus einem Objekt entfernen :

const doSomething = (obj, prop) => {
  let {[prop]: omit, ...res} = obj
  return res
}

Wenn der zu entfernende Eigenschaftsname statisch ist, können Sie ihn mit einem einfachen Einzeiler entfernen:

let {lastname, ...o2} = o

Der einfachste Weg ist einfach: Oder Sie können Ihr Objekt klonen, bevor Sie es mutieren:

const doSomething = (obj, prop) => {
  let res = Object.assign({}, obj)
  delete res[prop]
  return res
}

Alternativ können Sie die omitFunktion aus der lodashDienstprogrammbibliothek verwenden :

let o2 = _.omit(o, 'lastname')

Es ist als Teil des lodash- Pakets oder als eigenständiges lodash.omit- Paket erhältlich.

Leonid Beschastny
quelle
3
Ist das wirklich die einfachste Lösung? Bei Arrays können Sie beispielsweise a2 = a1.filter(el => el.id !== id)ein Element innerhalb eines Arrays durch eine bestimmte ID weglassen. Gibt es so etwas für ein Objekt nicht?
Amann
1
Ich stimme @amann zu. Entweder gibt es eine bessere Lösung, oder ES6 hat wirklich etwas übersehen, um JS funktionaler nutzbar zu machen. Z.B. Ruby hatkeep_if
Augustin Riedinger
1
@ AugustinRiedinger eigentlich gibt es einen Weg. Siehe mein Update.
Leonid Beschastny
Ich wollte dasselbe gemäß dieser Frage vorschlagen
Augustin Riedinger
1
Die aktualisierte Destrukturierungslösung ist großartig. Obwohl mein Verstand ein wenig davon geblasen ist. Warum const {[prop], ...rest} = objfunktioniert das nicht ? Warum müssen Sie die extrahierte Eigenschaft einer neuen Variablen zuweisen ( omitin diesem Fall)?
Kleiewe
27

Mit ES7-Objektzerstörung:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
Senbon
quelle
1
Was ist, wenn aes in einer Variablen gespeichert ist const x = 'a'?
FFF
Ändert nichts. a bezieht sich nur auf das erste Element des Arrays. Es könnte so sein: const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
Senbon
1
Dies ist die eleganteste und einfachste Lösung
Joe
1
Gibt es eine Möglichkeit, dies zu tun, wenn die Schlüssel Zahlen sind?
Marc Sloth Eastman
22

Einzeilige Lösung

const removeKey = (key, {[key]: _, ...rest}) => rest;
Punksta
quelle
4

Wie in den obigen Kommentaren vorgeschlagen, wenn Sie dies erweitern möchten, um mehr als ein Element aus Ihrem zu entfernen, das objectich gerne verwende filter. undreduce

z.B

    const o = {
      "firstname": "Jane",
      "lastname": "Doe",
      "middlename": "Kate",
      "age": 23,
      "_id": "599ad9f8ebe5183011f70835",
      "index": 0,
      "guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
      "isActive": true,
      "balance": "$1,510.89",
      "picture": "http://placehold.it/32x32",
      "eyeColor": "green",
      "registered": "2014-08-17T09:21:18 -10:00",
      "tags": [
        "consequat",
        "ut",
        "qui",
        "nulla",
        "do",
        "sunt",
        "anim"
      ]
    };

    const removeItems = ['balance', 'picture', 'tags']
    console.log(formatObj(o, removeItems))

    function formatObj(obj, removeItems) {
      return {
        ...Object.keys(obj)
          .filter(item => !isInArray(item, removeItems))
          .reduce((newObj, item) => {
            return {
              ...newObj, [item]: obj[item]
            }
          }, {})
      }
    }

    function isInArray(value, array) {
      return array.indexOf(value) > -1;
    }

ak85
quelle
1
Warum wenden Sie die Filterbedingung nicht in der Reduzierung an, damit Sie sich eine Schleife sparen können?
Ivo Sabev
danke für den Tipp @IvoSabev Ich habe ein paar Funktionen in meinem Code, bei denen ich filtere und dann reduziere, aber ich werde Ihren Vorschlag berücksichtigen, um die Schleife zu speichern.
Ak85
3

Um etwas Würze hinzuzufügen, die Leistung bringt. Überprüfen Sie diesen Faden unten

https://github.com/googleapis/google-api-nodejs-client/issues/375

Die Verwendung des Löschoperators hat leistungsnegative Auswirkungen auf das Muster der versteckten V8-Klassen. Im Allgemeinen wird empfohlen, es nicht zu verwenden.

Alternativ können wir zum Entfernen objektaufzählbarer Eigenschaften eine neue Objektkopie ohne diese Eigenschaften erstellen (Beispiel mit lodash):

_.omit (o, 'prop', 'prop2')

Oder definieren Sie den Eigenschaftswert auf null oder undefiniert (was bei der Serialisierung in JSON implizit ignoriert wird):

o.prop = undefiniert

Sie können auch den zerstörerischen Weg verwenden

const {remov1, remov2, ...new} = old;
old = new;

Und ein praktischeres Beispiel:

this._volumes[this._minCandle] = undefined;
{ 
     const {[this._minCandle]: remove, ...rest} = this._volumes;
     this._volumes = rest; 
}

Wie Sie sehen, können Sie die [somePropsVarForDynamicName]: scopeVarNameSyntax für dynamische Namen verwenden. Und Sie können alles in Klammern setzen (neuer Block), so dass der Rest Müll ist, der danach gesammelt wird.

Hier ein Test: Geben Sie hier die Bildbeschreibung ein

exec:

Geben Sie hier die Bildbeschreibung ein

Oder wir können mit einer Funktion wie gehen

function deleteProps(obj, props) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

für Typoskript

function deleteProps(obj: Object, props: string[]) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

Verwendung:

let a = {propH: 'hi', propB: 'bye', propO: 'ok'};

a = deleteProps(a, 'propB'); 

// or 

a = deleteProps(a, ['propB', 'propO']);

Auf diese Weise wird ein neues Objekt erstellt. Und die schnelle Eigenschaft des Objekts bleibt erhalten. Welches kann wichtig oder wichtig sein. Wenn auf das Mapping und das Objekt viele Male zugegriffen wird.

Auch das Assoziieren undefinedkann ein guter Weg sein. Wenn Sie es sich leisten können. Und für die Schlüssel können Sie auch den Wert überprüfen. Um beispielsweise alle aktiven Schlüssel zu erhalten, gehen Sie wie folgt vor:

const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.

Undefiniert ist jedoch nicht für große Listen geeignet. Oder Entwicklung im Laufe der Zeit mit vielen Requisiten. Da die Speichernutzung weiter zunimmt und niemals bereinigt wird. Es kommt also auf die Nutzung an. Und nur ein neues Objekt zu erstellen, scheint der gute Weg zu sein.

Dann setzt der Premature optimization is the root of all evilWille ein. Sie müssen sich also des Kompromisses bewusst sein. Und was benötigt wird und was nicht.

Anmerkung zu _.omit () von lodash

Es wurde aus Version 5 entfernt. Sie können es nicht im Repo finden. Und hier ein Thema, das darüber spricht.

https://github.com/lodash/lodash/issues/2930

v8

Sie können dies überprüfen, was eine gute Lektüre ist: https://v8.dev/blog/fast-properties

Mohamed Allal
quelle
0

mit lodash cloneDeep und löschen

(Hinweis: Der lodash-Klon kann stattdessen für flache Objekte verwendet werden.)

const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'

const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}
F F F
quelle
0

Für meinen Code wollte ich eine Kurzversion für den Rückgabewert von map (), aber die Multiline / Mutli-Operations-Lösungen waren "hässlich". Das Hauptmerkmal ist das alte, void(0)das sich auflöst undefined.

let o2 = {...o, age: 31, lastname: void(0)};

Die Eigenschaft bleibt im Objekt:

console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}

aber das Sende-Framework tötet es für mich (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Jonny
quelle
0

Mein Problem mit der akzeptierten Antwort aus einem ESLint-Regelstandard, wenn Sie versuchen zu zerstören:

    const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };

die 2 neuen Variablen notNeededund alsoNotNeededkönnen abhängig von Ihrem Setup eine Warnung oder einen Fehler auslösen, da sie jetzt nicht mehr verwendet werden. Warum also neue Vars erstellen, wenn sie nicht verwendet werden?

Ich denke, Sie müssen die deleteFunktion wirklich nutzen.

Phil Lucks
quelle
3
Die no-unused-varsRegel hat ignoreRestSiblingsjetzt tatsächlich die Option , diesen Anwendungsfall abzudecken
amann