Jak sklonować obiekt JavaScript z wyjątkiem jednego klucza?


307

Mam płaski obiekt JS:

{a: 1, b: 2, c: 3, ..., z:26}

Chcę sklonować obiekt z wyjątkiem jednego elementu:

{a: 1, c: 3, ..., z:26}

Jaki jest najłatwiejszy sposób na zrobienie tego (wolę używać ES6 / 7, jeśli to możliwe)?


Bez zmiany oryginalnego obiektu: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))
infinity1975

Odpowiedzi:


440

Jeśli używasz Babel , możesz użyć następującej składni, aby skopiować właściwość bz x do zmiennej b, a następnie skopiować pozostałe właściwości do zmiennej y :

let x = {a: 1, b: 2, c: 3, z:26};
let {b, ...y} = x;

i zostanie przełożony na:

"use strict";

function _objectWithoutProperties(obj, keys) {
  var target = {};
  for (var i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
}

var x = { a: 1, b: 2, c: 3, z: 26 };
var b = x.b;

var y = _objectWithoutProperties(x, ["b"]);

58
Jeśli podszyjesz kod w poszukiwaniu nieużywanych zmiennych, spowoduje to „Nieużywaną zmienną„ b ”. jednak ostrzeżenie.
Ross Allen

1
jak wyglądałaby składnia, gdybyś miałlet x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];
ke3pup

14
@RossAllen Istnieje opcja, ignoreRestSiblingsktóra została dodana w wersji 3.15.0 (3 lutego 2017 r.). Zobacz: commit c59a0ba
Ilya Palkin

4
@IlyaPalkin Ciekawe. Jest to jednak trochę leniwe, ponieważ nie zmienia to faktu, że istnieje bzakres.
Ross Allen,

2
Jeśli otrzymujesz kompilację modułu nie powiodło się: Błąd składni: nieoczekiwany token, prawdopodobnie musisz dodać wtyczkę transformacji rozprzestrzeniania babel. Zobacz babeljs.io/docs/plugins/transform-object-rest-spread
jsaven 20.01.2018

133
var clone = Object.assign({}, {a: 1, b: 2, c: 3});
delete clone.b;

lub jeśli akceptujesz własność jako niezdefiniowaną:

var clone = Object.assign({}, {a: 1, b: 2, c: 3}, {b: undefined});

7
Po prostu usunięcie właściwości jest jasnym i prostym sposobem na zrobienie tego.
pokaz

24
Zastrzeżenie dotyczące usuwania polega na tym, że nie jest to niezmienna operacja.
Javid Jamae,

1
to utrzymuje klucz
Fareed Alnamrouti

1
Mój komentarz został napisany przed edycją odpowiedzi i dodaniem instrukcji usuwania
Fareed Alnamrouti

Właśnie tego szukałem, ale nieco bardziej zaimplementowałem w nieco inny sposób: var cake = {... currentCake, requestorId: undefined};
abelito,

73

Aby dodać do odpowiedzi Ilyi Palkin: możesz nawet dynamicznie usuwać klucze:

const x = {a: 1, b: 2, c: 3, z:26};

const objectWithoutKey = (object, key) => {
  const {[key]: deletedKey, ...otherKeys} = object;
  return otherKeys;
}

console.log(objectWithoutKey(x, 'b')); // {a: 1, c: 3, z:26}
console.log(x); // {a: 1, b: 2, c: 3, z:26};

Demo w Babel REPL

Źródło:


3
To niezła składnia.
Hinrich

2
To świetnie, ale czy jest sposób, aby uniknąć nieużywanego var deleteKey? Nie dlatego, że powoduje problemy, ale sprawia, że ​​JSHint narzeka i wydaje się dziwny, ponieważ tak naprawdę go nie używamy.
Johnson Wong,

6
@JohnsonWong Co powiesz na użycie _dozwolonej zmiennej, której nie zamierzasz używać?
Ryan H.,

var b = {a:44, b:7, c:1}; let {['a']:z, ...others} = b; console.log(z , others ); // logs: 44, {b:7, c:1}
styczeń

70

Dla tych, którzy nie mogą korzystać z ES6, możesz użyć lodashlub underscore.

_.omit(x, 'b')

Lub ramda.

R.omit('b', x)

6
czy nie byłoby bardziej logiczne użycie tutaj pominięcia ? _.omit(x, 'b')
tibalt

Dzięki @tibalt. Zaktualizowałem odpowiedź o to.
Noel Llevares

usunięcie jest bardziej zwięzłe - użycie lodash, podkreślenia lub ramdy dotyczy tylko projektów, które już je wykorzystują i znają, w przeciwnym razie staje się to coraz bardziej nieistotne w 2018 r. i później.
Jimmont

1
@jimmont delete jest już wspomniany w innych odpowiedziach. Nie ma potrzeby duplikowania odpowiedzi, nie sądzisz? I oczywiście dotyczy to tylko tych, którzy już używają lodash lub ramda. Dotyczy to również tych, którzy utknęli w ES5 i wcześniejszych, jak stwierdzono w tej odpowiedzi.
Noel Llevares,

@dashmug mój poprzedni komentarz był krytyką podejścia (a nie twojej odpowiedzi), na które należy zwrócić uwagę przy wyborze podejścia przedstawionego w tej odpowiedzi. Chciałbym uzyskać te informacje, gdybym czytał odpowiedzi i jest to powód do dodania mojego komentarza i wzmianki delete.
Jimmont,

63

Używam tej wkładki ESNext

const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = (({ b, c, ...o }) => o)(obj) // remove b and c
console.log(clone)


Jeśli potrzebujesz funkcji ogólnego przeznaczenia:

function omit(obj, props) {
  props = props instanceof Array ? props : [props]
  return eval(`(({${props.join(',')}, ...o}) => o)(obj)`)
}

// usage
const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = omit(obj, ['b', 'c'])
console.log(clone)


9
Zamiast owijania w tablicę i mapmożesz zrobić:(({b, c, ...others}) => ({...others}))(obj)
bucabay

@bucabay: Genialny! To najlepsza odpowiedź z wielu! Zgodny ze standardami, działa idealnie w Węzle itp. Należy przesłać jako odpowiedź.
david.pfx

3
Fantastyczna odpowiedź kolego .. <3
Ajithkumar S

5
@totymedli: Nie przeze mnie. Przyjmę formę syntaktyczną, część standardowego ES6, zamiast funkcji magicznej w dowolnym momencie, ze względu na czytelność.
david.pfx

1
Znakomity. To wszystko.
darksoulsong

22

Możesz napisać dla niego prostą funkcję pomocniczą. Lodash ma podobną funkcję o tej samej nazwie: pomiń

function omit(obj, omitKey) {
  return Object.keys(obj).reduce((result, key) => {
    if(key !== omitKey) {
       result[key] = obj[key];
    }
    return result;
  }, {});
}

omit({a: 1, b: 2, c: 3}, 'c')  // {a: 1, b: 2}

Pamiętaj też, że jest szybszy niż Object.assign, a następnie usuń: http://jsperf.com/omit-key


11

Może coś takiego:

var copy = Object.assign({}, {a: 1, b: 2, c: 3})
delete copy.c;

Czy to wystarczy? A może nie można cgo skopiować?


11

Korzystanie z destrukcji obiektów

const omit = (prop, { [prop]: _, ...rest }) => rest;
const obj = { a: 1, b: 2, c: 3 };
const objWithoutA = omit('a', obj);
console.log(objWithoutA); // {b: 2, c: 3}


Świetne rozwiązanie!
Denys Mikhalenko

2
Myślę, że to rozwiązanie ma zapobiec ostrzeżeniu „Nieużywana zmienna” w JSLint. Niestety używanie _nie rozwiązuje problemu dla ESLint ...
bert bruynooghe

6

Hej, wygląda na to, że napotykasz problemy, gdy próbujesz skopiować obiekt, a następnie usunąć właściwość. Gdzieś trzeba przypisać prymitywne zmienne, aby javascript tworzył nową wartość.

To była prosta sztuczka (może być przerażająca)

var obj = {"key1":"value1","key2":"value2","key3":"value3"};

// assign it as a new variable for javascript to cache
var copy = JSON.stringify(obj);
// reconstitute as an object
copy = JSON.parse(copy);
// now you can safely run delete on the copy with completely new values
delete copy.key2

console.log(obj)
// output: {key1: "value1", key2: "value2", key3: "value3"}
console.log(copy)
// output: {key1: "value1", key3: "value3"}

Właściwie to mi się podoba. Mógłbym zrobić JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Nawet nie musisz go usuwać, wystarczy wartość falsy.
Czad

6

Oto opcja pominięcia kluczy dynamicznych, które moim zdaniem nie zostały jeszcze wspomniane:

const obj = { 1: 1, 2: 2, 3: 3, 4: 4 };
const removeMe = 1;

const { [removeMe]: removedKey, ...newObj } = obj;

removeMejest aliasowany removedKeyi ignorowany. newObjstaje się { 2: 2, 3: 3, 4: 4 }. Zauważ, że usunięty klucz nie istnieje, wartość nie została tylko ustawiona na undefined.


Niezłe rozwiązanie. Miałem podobny przypadek użycia (dynamiczny klucz w reduktorze).
ericgio

4

NAJPROSTSZY SPOSÓB

const allAlphabets = {a: 1, b: 2, c: 3, ..., z:26};
const { b, ...allExceptOne } = allAlphabets;
console.log(allExceptOne);   // {a: 1, c: 3, ..., z:26}

3

Lodash pomiń

let source = //{a: 1, b: 2, c: 3, ..., z:26}
let copySansProperty = _.omit(source, 'b');
// {a: 1, c: 3, ..., z:26}

tak, Lodash to elegancka opcja, ale starałem się osiągnąć to samo z wanilią ES6
andreasonny83

3

W tym celu możesz również użyć operatora rozprzestrzeniania

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }

2
Wygląda na fajne. To jednak utrzymuje klucz jako niezdefiniowany wcopy
kano

więc możesz usunąć niezdefiniowane klucze, jeśli są tylko te, które się tam znajdują
Victor

1
nie jestem pewien, dlaczego dodatkowo rozłożyłeś w kopii, const copy = { ...source, b: undefined } sprowadza się do dokładnie tego samego.
bert bruynooghe

3

Powyższe rozwiązania wykorzystujące strukturalizację cierpią na to, że masz używaną zmienną, co może powodować skargi ze strony ESLint, jeśli z niej korzystasz.

Oto moje rozwiązania:

const src = { a: 1, b: 2 }
const result = Object.keys(src)
  .reduce((acc, k) => k === 'b' ? acc : { ...acc, [k]: src[k] }, {})

Na większości platform (oprócz IE, chyba że używasz Babel), możesz także:

const src = { a: 1, b: 2 }
const result = Object.fromEntries(
  Object.entries(src).filter(k => k !== 'b'))


2

Jeśli masz do czynienia z dużą zmienną, nie chcesz jej kopiować, a następnie usuwać, ponieważ byłoby to nieefektywne.

Prosta pętla for z funkcją hasOwnProperty powinna działać i jest o wiele bardziej przystosowalna do przyszłych potrzeb:

for(var key in someObject) {
        if(someObject.hasOwnProperty(key) && key != 'undesiredkey') {
                copyOfObject[key] = someObject[key];
        }
}

1
Rozwiązanie operatora rozprzestrzeniania robi dokładnie to samo z dużo ładniejszą składnią.
david.pfx

2

A co z tym? Nigdy nie znalazłem tego tupotu, ale próbowałem po prostu wykluczyć jedną lub więcej właściwości bez potrzeby tworzenia dodatkowego obiektu. Wydaje się, że to działa, ale są pewne skutki uboczne, których nie jestem w stanie zobaczyć. Na pewno nie jest bardzo czytelny.

const postData = {
   token: 'secret-token',
   publicKey: 'public is safe',
   somethingElse: true,
};

const a = {
   ...(({token, ...rest} = postData) => (rest))(),
}

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/

2

Dokonałem tego w ten sposób, jako przykład z mojego reduktora Redux:

 const clone = { ...state };
 delete clone[action.id];
 return clone;

Innymi słowy:

const clone = { ...originalObject } // note: original object is not altered
delete clone[unwantedKey]           // or use clone.unwantedKey or any other applicable syntax
return clone                        // the original object without the unwanted key

Wydaje się, że to dużo dodatkowej pracy, w porównaniu do zaakceptowanej odpowiedzi sprzed 3 lat (i obie opcje polegają na transpozycji do wielu przeglądarek).
carpiediem

Mam podobny przypadek użycia (reduktor), więc wymyśliłem fajny sposób, który obsługuje klucze dynamiczne bez mutacji. Zasadniczo: const { [removeMe]: removedKey, ...newObj } = obj;- patrz moja odpowiedź na to pytanie.
goldins

1

Ostatnio zrobiłem to w bardzo prosty sposób:

const obj = {a: 1, b: 2, ..., z:26};

wystarczy użyć operatora rozkładania, aby oddzielić niepożądaną właściwość:

const {b, ...rest} = obj;

... i object.ignign, aby wziąć tylko część „odpoczynku”:

const newObj = Object.assign({}, {...rest});

3
restjest już nowym obiektem - nie potrzebujesz ostatniej linii. Ponadto jest to identyczne z przyjętym rozwiązaniem.
carpiediem

0
const x = {obj1: 1, pass: 2, obj2: 3, obj3:26};

const objectWithoutKey = (object, key) => {
  const {[key]: deletedKey, ...otherKeys} = object;
  return otherKeys;
}

console.log(objectWithoutKey(x, 'pass'));

1
Chociaż ten kod może dostarczyć rozwiązania pytania, lepiej jest dodać kontekst wyjaśniający, dlaczego / jak to działa. Może to pomóc przyszłym użytkownikom w nauce i zastosowaniu tej wiedzy do własnego kodu. Prawdopodobnie będziesz mieć pozytywne opinie od użytkowników w postaci pozytywnych opinii, gdy kod zostanie wyjaśniony.
borchvm
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.