ลบคุณสมบัติในวัตถุที่ไม่เปลี่ยนแปลง


145

ฉันใช้ Redux ในตัวลดของฉันฉันกำลังพยายามลบคุณสมบัติออกจากวัตถุเช่นนี้:

const state = {
    a: '1',
    b: '2',
    c: {
       x: '42',
       y: '43'
    },
}

และฉันต้องการมีบางอย่างเช่นนี้โดยไม่ต้องเปลี่ยนสถานะเดิม:

const newState = {
    a: '1',
    b: '2',
    c: {
       x: '42',
    },
}

ฉันเหนื่อย:

let newState = Object.assign({}, state);
delete newState.c.y

แต่ด้วยเหตุผลบางอย่างมันจะลบคุณสมบัติจากทั้งสองรัฐ

ช่วยฉันทำอย่างนั้นได้ไหม


3
โปรดทราบว่าการObject.assignสร้างเพียงสำเนาตื้นของstateและดังนั้นจึงstate.cและnewState.cจะชี้ไปยังวัตถุที่ใช้ร่วมกันเดียวกัน คุณพยายามที่จะลบคุณสมบัติyจากวัตถุที่ใช้ร่วมกันและไม่ได้มาจากวัตถุใหม่c newState
Akseli Palén

คำตอบ:


224

วิธีการเกี่ยวกับการใช้ไวยากรณ์มอบหมายงาน destructuring ?

const original = {
  foo: 'bar',
  stack: 'overflow',
};

// If the name of the property to remove is constant
const { stack, ...withoutFirst } = original;
console.log(withoutFirst); // Will be { "foo": "bar" }

// If the name of the property to remove is from a variable
const key = 'stack'
const { [key]: value, ...withoutSecond } = original;
console.log(withoutSecond); // Will be { "foo": "bar" }

// To do a deep removal with property names from variables
const deep = {
  foo: 'bar',
  c: {
   x: 1,
   y: 2
  }
};

const parentKey = 'c';
const childKey = 'y';
// Remove the 'c' element from original
const { [parentKey]: parentValue, ...noChild } = deep;
// Remove the 'y' from the 'c' element
const { [childKey]: removedValue, ...childWithout } = parentValue;
// Merge back together
const withoutThird = { ...noChild, [parentKey]: childWithout };
console.log(withoutThird); // Will be { "foo": "bar", "c": { "x": 1 } }


3
ยอดเยี่ยมโดยไม่มีห้องสมุดเพิ่มเติมใช้ประโยชน์จาก ES6 และเรียบง่ายพอ
sbk201

ทางออกที่หรูหราที่สุด
ยาว.Luc

12
ดี! นี่คือฟังก์ชั่นผู้ช่วย ES6 const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;ไปพร้อมกับมัน การใช้งาน: deleteProperty({a:1, b:2}, "a");ให้{b:2}
mikebridge

ฉลาดมากฉันมาสายนี้ แต่เป็นหนึ่งในรายการโปรดใหม่ของฉัน
Gavin

1
โปรดทราบว่าตัวอย่างการลบแบบลึกจะมีปัญหาหากdeep['c']ว่างเปล่าดังนั้นในกรณีทั่วไปคุณอาจต้องการเพิ่มการตรวจสอบการมีอยู่ของคีย์
Laurent S

46

ผมพบว่าวิธีการอาร์เรย์ ES5 เหมือนfilter, mapและreduceประโยชน์เพราะพวกเขามักจะกลับอาร์เรย์ใหม่หรือวัตถุ ในกรณีนี้ฉันจะใช้วนObject.keysซ้ำวัตถุและArray#reduceเปลี่ยนกลับเป็นวัตถุ

return Object.assign({}, state, {
    c: Object.keys(state.c).reduce((result, key) => {
        if (key !== 'y') {
            result[key] = state.c[key];
        }
        return result;
    }, {})
});

การเปลี่ยนแปลงเล็กน้อยของฉันเพื่อให้ชัดเจนยิ่งขึ้น IMO ... ยังช่วยให้คุณละเว้นคุณสมบัติหลายอย่าง const omit = ['prop1', 'prop2'] ... ถ้า (omit.indexOf (กุญแจ) === -1) ผลลัพธ์ [key] = state.c [key] ผลตอบแทน; ...
josh-sachs

3
เทียบเท่า ES6 เพื่อรับสำเนาของmyObjectคีย์เมื่อmyKeyลบออก:Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang

38

คุณสามารถใช้_.omit(object, [paths])จากห้องสมุดlodash

เส้นทางสามารถซ้อนกันเช่น: _.omit(object, ['key1.key2.key3'])


3
น่าเสียดายที่_.omitไม่สามารถลบคุณสมบัติที่ลึก (OP ที่ขอ) มีomit-deep-lodashโมดูลสำหรับจุดประสงค์นี้
AlexM

2
ออกไปในสิ่งที่ @AlexM พูด ฉันพบว่ามีประโยชน์และอาจเหมาะสมกว่าสำหรับเรา_.cloneDeep(obj)จาก lodash ที่คัดลอกวัตถุได้อย่างง่ายดายแล้วคุณสามารถใช้ js delete obj.[key]เพื่อลบคีย์ได้
Alex J

เห็นด้วย w / @AlexJ, cloneDeep ทำงานได้อย่างสมบูรณ์และคุณสามารถใช้ไวยากรณ์การแพร่กระจายได้เช่นกัน: ..._. cloneDeep (state) สำหรับ Redux
Sean Chase

32

เพียงใช้คุณสมบัติการทำลายวัตถุ ES6

const state = {
    c: {
       x: '42',
       y: '43'
    },
}

const { c: { y, ...c } } = state // generates a new 'c' without 'y'

console.log({...state, c }) // put the new c on a new state


8
const {y, ...c} = state.cอาจชัดเจนกว่าการมีสองcทางด้านซ้ายมือ
dosentmatter

10
ระวัง: นี่ใช้ไม่ได้กับอ็อบเจกต์ที่มีเลขจำนวนเต็ม
daviestar

3
จากคำตอบด้านล่างหากคุณต้องการอ้างอิงชื่อตัวแปรที่จะลบ: const name = 'c'จากนั้นคุณสามารถทำเช่นconst {[name]:deletedValue, ...newState} = stateนั้นกลับมาnewStateใน reducer ของคุณ นี่คือการลบคีย์ระดับสูงสุด
FFF

23

นั่นเป็นเพราะคุณกำลังคัดลอกค่าของstate.cไปยังวัตถุอื่น และค่านั้นเป็นตัวชี้ไปยังวัตถุจาวาสคริปต์อื่น ดังนั้นพอยน์เตอร์ทั้งสองชี้ไปที่วัตถุเดียวกัน

ลองสิ่งนี้:

let newState = Object.assign({}, state);
console.log(newState == state); // false
console.log(newState.c == state.c); // true
newState.c = Object.assign({}, state.c);
console.log(newState.c == state.c); // now it is false
delete newState.c.y;

นอกจากนี้คุณยังสามารถทำสำเนาลึกของวัตถุ ดูคำถามนี้แล้วคุณจะพบว่ามีอะไรที่ดีที่สุดสำหรับคุณ


2
นี่คือคำตอบที่ยอดเยี่ยม! state.cคือการอ้างอิงและการอ้างอิงจะถูกคัดลอกได้ดี Redux ต้องการรูปร่างสถานะปกติซึ่งหมายถึงการใช้รหัสแทนการอ้างอิงเมื่อรัฐทำรัง ตรวจสอบเอกสาร redux: redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Ziggy

17

เกี่ยวกับสิ่งนี้:

function removeByKey (myObj, deleteKey) {
  return Object.keys(myObj)
    .filter(key => key !== deleteKey)
    .reduce((result, current) => {
      result[current] = myObj[current];
      return result;
  }, {});
}

มันกรองกุญแจที่ควรจะถูกลบจากนั้นสร้างวัตถุใหม่จากกุญแจที่เหลือและวัตถุเริ่มต้น ความคิดถูกขโมยไปจากโปรแกรมปฏิกิริยาตอบสนองที่ยอดเยี่ยมของ Tyler McGinnes

JSBin


11
function dissoc(key, obj) {
  let copy = Object.assign({}, obj)
  delete copy[key]
  return copy
}

นอกจากนี้ถ้ามองหาการเขียนโปรแกรมเครื่องมือการทำงานให้ดูที่Ramda


9

คุณสามารถใช้ผู้ช่วยที่ไม่สามารถเปลี่ยนแปลงได้เพื่อยกเลิกการกำหนดแอตทริบิวต์ในกรณีของคุณ:

import update from 'immutability-helper';

const updatedState = update(state, {
  c: {
    $unset: ['y']
  }
});    

ถ้าเช่นนั้นจะลบคุณสมบัติ "y" ของทุกรายการในอาเรย์วัตถุได้อย่างไร
5ervant

@ 5 ผู้รับฉันคิดว่านั่นเป็นคำถามอื่น แต่ฉันขอแนะนำให้คุณทำแผนที่อาร์เรย์และนำไปใช้แก้ปัญหาที่กำหนดที่นี่
Javier P

8

ตั้งแต่ 2019 ตัวเลือกอื่นคือการใช้Object.fromEntriesวิธีการ มันมาถึงขั้นตอนที่ 4

const newC = Object.fromEntries(
    Object.entries(state.c).filter(([key]) => key != 'y')
)
const newState = {...state, c: newC}

สิ่งที่ดีเกี่ยวกับมันก็คือมันสามารถจัดการกับคีย์จำนวนเต็มได้อย่างดี



3

ปัญหาที่คุณมีคือคุณไม่ได้ทำการโคลนนิ่งสถานะเริ่มต้นอย่างลึกซึ้ง ดังนั้นคุณมีสำเนาตื้น

คุณสามารถใช้โอเปอเรเตอร์การแพร่กระจาย

  const newState = { ...state, c: { ...state.c } };
  delete newState.c.y

หรือทำตามรหัสเดียวกัน

let newState = Object.assign({}, state, { c: Object.assign({}, state.c) });
delete newState.c.y

1

ปกติฉันจะใช้

Object.assign({}, existingState, {propToRemove: undefined})

ฉันรู้ว่านี่ไม่ใช่การลบอสังหาริมทรัพย์ แต่สำหรับวัตถุประสงค์เกือบทั้งหมด1มันเทียบเท่ากับการใช้งานจริง ไวยากรณ์สำหรับเรื่องนี้ง่ายกว่าทางเลือกที่ฉันรู้สึกว่าเป็นการแลกเปลี่ยนที่ดีทีเดียว

1ถ้าคุณใช้hasOwnProperty()คุณจะต้องใช้วิธีแก้ปัญหาที่ซับซ้อนมากขึ้น


1

ฉันใช้รูปแบบนี้

const newState = Object.assign({}, state);
      delete newState.show;
      return newState;

แต่ในหนังสือฉันเห็นรูปแบบอื่น

return Object.assign({}, state, { name: undefined } )

คนที่สองไม่ได้ลบกุญแจ มันเพียงแค่กำหนดเป็นไม่ได้กำหนด
bman

1

ยูทิลิตี้;))

const removeObjectField = (obj, field) => {

    // delete filter[selectName]; -> this mutates.
    const { [field]: remove, ...rest } = obj;

    return rest;
}

ประเภทการกระทำ

const MY_Y_REMOVE = 'MY_Y_REMOVE';

ผู้สร้างการกระทำ

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

        return dispatch =>
            dispatch({
                type: MY_Y_REMOVE,
                payload: result
            })
    }

ลด

export default (state ={}, action) => {
  switch (action.type) {
    case myActions.MY_Y_REMOVE || :
      return { ...state, c: action.payload };
    default:
      return state;
  }
};

0

ดังที่ได้กล่าวไว้ในคำตอบบางข้อแล้วเนื่องจากคุณกำลังพยายามปรับเปลี่ยนสถานะซ้อนกันเช่น ลึกลงไปหนึ่งระดับ วิธีแก้ปัญหาที่ยอมรับได้คือการเพิ่มตัวลดในxระดับรัฐ:

const state = {
    a: '1',
    b: '2',
    c: {
       x: '42',
       y: '43'
    },
}

ตัวลดระดับที่ลึกกว่า

let newDeepState = Object.assign({}, state.c);
delete newDeepState.y;

ตัวลดระดับเดิม

let newState = Object.assign({}, state, {c: newDeepState});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.