ลบค่าออกจากวัตถุโดยไม่มีการกลายพันธุ์


105

อะไรคือวิธีที่ดีและสั้นในการลบค่าออกจากวัตถุที่คีย์เฉพาะโดยไม่ทำให้วัตถุดั้งเดิมกลายพันธุ์

ฉันต้องการทำสิ่งที่ชอบ:

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

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

เช่นในการเพิ่มมูลค่าฉันทำสิ่งต่อไปนี้:

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

ค่อนข้างสั้นจำง่ายและไม่ต้องการฟังก์ชันยูทิลิตี้

มีสิ่งนี้สำหรับการลบค่าหรือไม่? ES6 ยินดีต้อนรับเป็นอย่างยิ่ง

ขอบคุณมาก!


"ลบค่าโดยไม่ทำให้วัตถุกลายพันธุ์" ไม่สมเหตุสมผล คุณไม่สามารถลบออกจากบางสิ่งและเก็บไว้ในสภาพเดิมได้ สิ่งที่คุณกำลังทำคือการทำสำเนาบางส่วน
JJJ

คำตอบ:


231

อัปเดต:

คุณสามารถลบคุณสมบัติออกจากวัตถุด้วยการกำหนดโครงสร้างการทำลายที่ยุ่งยาก:

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

แม้ว่าชื่อคุณสมบัติที่คุณต้องการลบเป็นแบบคงที่คุณสามารถลบออกได้ด้วยซับเดียวง่ายๆ:

let {lastname, ...o2} = o

วิธีที่ง่ายที่สุดคือหรือคุณสามารถโคลนวัตถุของคุณก่อนที่จะกลายพันธุ์:

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

หรือคุณสามารถใช้omitฟังก์ชันจากlodashไลบรารียูทิลิตี้ :

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

มีให้บริการเป็นส่วนหนึ่งของแพ็คเกจlodashหรือเป็นแพ็คเกจlodash.omitแบบสแตนด์อโลน


3
นั่นเป็นวิธีแก้ปัญหาที่ง่ายที่สุดจริงหรือ? เช่นสำหรับอาร์เรย์คุณสามารถทำได้a2 = a1.filter(el => el.id !== id)เพื่อละเว้นองค์ประกอบภายในอาร์เรย์โดยใช้รหัสเฉพาะ ไม่มีสิ่งนั้นสำหรับวัตถุ?
amann

1
ฉันเห็นด้วยกับ @amann มีทั้งเป็นทางออกที่ดีกว่าหรือ ES6 พลาดจริงๆบางสิ่งบางอย่างเกี่ยวกับการทำ JS ใช้งานในทางที่ทำงานได้มากขึ้น เช่น. Ruby haskeep_if
Augustin Riedinger

1
@AugustinRiedinger จริงๆแล้วมีวิธี ดูการอัปเดตของฉัน
Leonid Beschastny

2
โซลูชันการทำลายล้างที่อัปเดตนั้นยอดเยี่ยมมาก แม้ว่าจิตใจของฉันจะถูกพัดไปเล็กน้อย ทำไมจะไม่const {[prop], ...rest} = objทำงาน? เหตุใดคุณจึงต้องกำหนดคุณสมบัติที่แยกออกมาให้กับตัวแปรใหม่ ( omitในกรณีนี้)
รำเว็บ

1
@ Antoni4 คุณสามารถเพิ่ม "ละเว้น" หรือ "ละ" ในno-unused-varsกฎของคุณได้มันเป็นเพียงนิพจน์ทั่วไป
adrianmc

37

ด้วยการทำลายโครงสร้างวัตถุ ES7:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }

1
เกิดอะไรขึ้นถ้าaถูกเก็บไว้ในตัวแปรconst x = 'a'?
FFF

ไม่มีอะไรเปลี่ยนแปลง a หมายถึงองค์ประกอบแรกของอาร์เรย์เท่านั้น อาจเป็นเช่นนี้ const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
senbon

1
นี่เป็นทางออกที่เรียบง่ายและสง่างามที่สุด
โจ

1
มีวิธีการทำเช่นนี้หรือไม่ถ้าคีย์เป็นตัวเลข?
Marc Sloth Eastman


4

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

เช่น

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


1
ทำไมคุณไม่ใช้เงื่อนไขตัวกรองในการลดเพื่อให้คุณสามารถช่วยตัวเองได้หนึ่งวง
Ivo Sabev

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

4

เพื่อเพิ่มเครื่องเทศที่นำมาซึ่งประสิทธิภาพ ตรวจสอบการร้องของกระทู้นี้

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

การใช้ตัวดำเนินการลบมีผลเสียด้านประสิทธิภาพสำหรับรูปแบบคลาส V8 ที่ซ่อนอยู่ โดยทั่วไปไม่แนะนำให้ใช้

อีกวิธีหนึ่งในการลบคุณสมบัติที่สามารถระบุได้ของตัวเองเราสามารถสร้างสำเนาวัตถุใหม่โดยไม่มีคุณสมบัติเหล่านั้น (ตัวอย่างเช่นการใช้ lodash):

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

หรือแม้กระทั่งกำหนดค่าคุณสมบัติเป็นค่าว่างหรือไม่ได้กำหนด (ซึ่งจะถูกละเว้นโดยปริยายเมื่อทำให้อนุกรมกับ JSON):

o.prop = ไม่ได้กำหนด

คุณสามารถใช้วิธีทำลายล้างมากเกินไป

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

และตัวอย่างที่เป็นประโยชน์มากขึ้น:

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

ดังที่คุณเห็นคุณสามารถใช้[somePropsVarForDynamicName]: scopeVarNameไวยากรณ์สำหรับชื่อไดนามิก และคุณสามารถใส่ทั้งหมดในวงเล็บ (บล็อกใหม่) ดังนั้นส่วนที่เหลือจะถูกเก็บรวบรวมหลังจากนั้น

นี่คือการทดสอบ: ใส่คำอธิบายภาพที่นี่

ผู้บริหาร:

ใส่คำอธิบายภาพที่นี่

หรือเราสามารถใช้ฟังก์ชันบางอย่างเช่น

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;
    }, {});
}

สำหรับ typescript

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;
    }, {});
}

การใช้งาน:

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

a = deleteProps(a, 'propB'); 

// or 

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

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

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

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.

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

จากนั้นPremature optimization is the root of all evilจะเริ่มต้นขึ้นดังนั้นคุณต้องระวังการแลกเปลี่ยน และสิ่งที่จำเป็นและสิ่งที่ไม่

หมายเหตุเกี่ยวกับ _.omit () จาก lodash

มันถูกลบออกจากเวอร์ชัน 5 คุณไม่พบมันใน repo และนี่คือประเด็นที่พูดถึง

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

v8

คุณสามารถตรวจสอบสิ่งนี้ซึ่งเป็นการอ่านที่ดีhttps://v8.dev/blog/fast-properties


1

ปัญหาของฉันกับคำตอบที่ยอมรับจากมาตรฐานกฎ ESLint หากคุณพยายามทำลายโครงสร้าง:

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

ตัวแปรใหม่ 2 ตัวnotNeededและalsoNotNeededอาจส่งคำเตือนหรือข้อผิดพลาดขึ้นอยู่กับการตั้งค่าของคุณเนื่องจากตอนนี้ไม่ได้ใช้งาน แล้วทำไมต้องสร้าง vars ใหม่ถ้าไม่ได้ใช้?

ฉันคิดว่าคุณต้องใช้deleteฟังก์ชันนี้อย่างแท้จริง


4
ตอนนี้no-unused-varsกฎมีตัวเลือกที่ignoreRestSiblingsจะครอบคลุมกรณีการใช้งานนี้: eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
amann

0

ด้วย lodash clone ลึกและลบ

(หมายเหตุ: สามารถใช้ lodash clone แทนวัตถุตื้น ๆ ได้)

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}

0

สำหรับรหัสของฉันฉันต้องการเวอร์ชันสั้นสำหรับค่าที่ส่งคืนของแผนที่ () แต่โซลูชันการดำเนินการหลายสาย / หลายสายนั้น "น่าเกลียด" คุณลักษณะที่สำคัญคือรุ่นเก่าvoid(0)ที่แก้ไขundefinedได้

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

คุณสมบัติยังคงอยู่ในวัตถุ:

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

แต่เฟรมเวิร์กการส่งฆ่ามันเพื่อฉัน (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.