ฉันจะโคลนวัตถุ JavaScript ยกเว้นรหัสเดียวได้อย่างไร


308

ฉันมีวัตถุ JS แบน:

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

ฉันต้องการโคลนวัตถุยกเว้นองค์ประกอบหนึ่ง:

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

วิธีที่ง่ายที่สุดในการทำเช่นนี้คืออะไร (เลือกที่จะใช้ es6 / 7 ถ้าเป็นไปได้)?


โดยไม่ต้องเปลี่ยนวัตถุต้นฉบับ: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))
infinity1975

คำตอบ:


441

หากคุณใช้Babelคุณสามารถใช้ไวยากรณ์ต่อไปนี้เพื่อคัดลอกคุณสมบัติ b จาก x ไปยังตัวแปร b แล้วคัดลอกคุณสมบัติที่เหลือลงในตัวแปร y :

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

และมันจะถูกเปลี่ยนเป็น:

"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
หากคุณขยี้โค้ดของคุณสำหรับตัวแปรที่ไม่ได้ใช้สิ่งนี้จะส่งผลให้ "ตัวแปรที่ไม่ได้ใช้" b " เตือนว่า
Ross Allen

1
ไวยากรณ์จะดูอย่างไรถ้าคุณมีlet x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];
ke3pup

14
@RossAllen มีตัวเลือกignoreRestSiblingsที่ถูกเพิ่มใน v3.15.0 (3 กุมภาพันธ์ 2017) ดู: กระทำ c59a0ba
Ilya Palkin

4
@IlyaPalkin ที่น่าสนใจ มันรู้สึกขี้เกียจนิดหน่อยเพราะมันไม่ได้เปลี่ยนความจริงที่ว่ามันมีbขอบเขต
Ross Allen

2
หากคุณได้รับการสร้างโมดูลล้มเหลว: SyntaxError: โทเค็นที่ไม่คาดคิดคุณอาจต้องเพิ่มปลั๊กอินการแปลงสเปรดส่วนที่เหลือของ babel ดู babeljs.io/docs/plugins/transform-object-rest-spread
jsaven

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

หรือถ้าคุณยอมรับคุณสมบัติที่จะไม่ได้กำหนด:

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

7
เพียงแค่การลบคุณสมบัติเป็นวิธีที่ชัดเจนและง่ายต่อการทำ
sshow

24
ข้อแม้ที่มีการลบคือมันไม่ได้เป็นการดำเนินการที่ไม่เปลี่ยนรูป
Javid Jamae

1
สิ่งนี้จะเป็นกุญแจสำคัญ
Fareed Alnamrouti

1
ความคิดเห็นของฉันถูกเขียนก่อนที่เขาจะแก้ไขคำตอบของเขาและเพิ่มคำสั่งลบ
Fareed Alnamrouti

นี่คือสิ่งที่ฉันกำลังมองหา แต่มีการใช้งานมากกว่าเล็กน้อยในวิธีที่แตกต่างกันเล็กน้อย: var cake = {... currentCake, requestorId: undefined};
abelito

73

หากต้องการเพิ่มคำตอบของ Ilya Palkin: คุณสามารถลบคีย์แบบไดนามิกได้:

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

การสาธิตใน Babel REPL

ที่มา:


3
นี่เป็นรูปแบบที่ดี
Hinrich

2
นี่เป็นวิธีที่ดี แต่มีวิธีที่จะหลีกเลี่ยง var deleteKey ที่ไม่ได้ใช้หรือไม่ ไม่ใช่ว่ามันทำให้เกิดปัญหา แต่ทำให้ JSHint บ่นและดูแปลกเพราะเราไม่ได้ใช้มัน
Johnson Wong

6
@JohnsonWong วิธีการใช้งาน_ที่อนุญาตให้ใช้สำหรับตัวแปรที่คุณไม่ต้องการใช้
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}
jimmont

70

สำหรับผู้ที่ไม่สามารถใช้ ES6 คุณสามารถใช้หรือlodashunderscore

_.omit(x, 'b')

ramdaหรือ

R.omit('b', x)

6
มันจะไม่มีเหตุผลมากกว่าที่จะใช้ละเว้นที่นี่หรือ _.omit(x, 'b')
tibalt

ขอบคุณ @tibalt อัปเดตคำตอบด้วย
Noel Llevares

การลบมีความกระชับมากขึ้น - การใช้ lodash, ขีดล่างหรือ ramda นั้นเกี่ยวข้องกับโครงการที่ใช้แล้วและรู้จักพวกเขาเท่านั้นมิฉะนั้นสิ่งนี้จะไม่เกี่ยวข้องมากขึ้นในปี 2561
jimmont

1
@jimmont delete ถูกกล่าวถึงแล้วในคำตอบอื่น ๆ ไม่จำเป็นต้องมีคำตอบซ้ำกันคุณไม่คิดเหรอ? และแน่นอนมันเกี่ยวข้องกับผู้ที่ใช้ lodash หรือ ramda อยู่แล้วเท่านั้น และมันก็เกี่ยวข้องกับสิ่งที่ติดอยู่กับ ES5 และรุ่นก่อนหน้าตามที่ระบุไว้ในคำตอบนี้
Noel Llevares

@dashmug ความคิดเห็นก่อนหน้าของฉันคือการวิจารณ์ของวิธีการ (ไม่ใช่คำตอบของคุณ) ที่ควรสังเกตเมื่อเลือกใช้วิธีที่แสดงในคำตอบนี้ deleteฉันต้องการข้อมูลนี้หากผมอ่านผ่านคำตอบและเหตุผลสำหรับการเพิ่มความคิดเห็นของฉันและการกล่าวขวัญ
jimmont

63

ฉันใช้ 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)


หากคุณต้องการฟังก์ชั่นวัตถุประสงค์ทั่วไป:

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
แทนที่จะห่อในอาร์เรย์และmapคุณสามารถทำได้:(({b, c, ...others}) => ({...others}))(obj)
bucabay

@bucabay: ยอดเยี่ยม! นั่นเป็นคำตอบที่ดีที่สุดของล็อต! เป็นไปตามมาตรฐานทำงานได้อย่างสมบูรณ์ในโหนด ฯลฯ คุณควรส่งคำตอบ
david.pfx

3
เพื่อนคำตอบที่ยอดเยี่ยม .. <3
Ajithkumar S

5
@totymedli: ไม่ใช่โดยฉัน ฉันจะใช้รูปแบบวากยสัมพันธ์ส่วนหนึ่งของ ES6 มาตรฐานฟังก์ชั่นเวทย์มนตร์ทุกที่ทุกเวลาในบริเวณที่สามารถอ่านได้
david.pfx

1
สุกใส นั่นคือทั้งหมดที่
darksoulsong

22

คุณสามารถเขียนฟังก์ชันตัวช่วยอย่างง่ายได้ Lodash มีฟังก์ชั่นที่คล้ายกันที่มีชื่อเดียวกัน: งด

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}

นอกจากนี้โปรดทราบว่ามันเร็วกว่า Object.assign และลบแล้ว: http://jsperf.com/omit-key


11

อาจจะเป็นสิ่งนี้:

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

ดีพอหรือไม่ หรือไม่สามารถcคัดลอกได้จริงเหรอ?


11

ใช้การทำลายวัตถุ

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}


สุดยอดทางออก!
Denys Mikhalenko

2
ฉันเดาว่าโซลูชันนี้มีวัตถุประสงค์เพื่อป้องกันคำเตือน 'ตัวแปรที่ไม่ได้ใช้' ใน JSLint น่าเสียดายที่การใช้_ไม่ได้ช่วยแก้ปัญหาสำหรับ ESLint ...
bert bruynooghe

6

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

เคล็ดลับง่าย ๆ (อาจจะน่ากลัว) ฉันเคยเป็นแบบนี้

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

ฉันชอบมันมาก JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));สามารถทำ ไม่จำเป็นต้องลบเลยเพียงแค่ต้องการค่าที่ผิดพลาด
ชาด

6

นี่คือตัวเลือกสำหรับการละเว้นคีย์แบบไดนามิกที่ฉันเชื่อว่ายังไม่ได้กล่าวถึง:

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

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

removeMeมีนามแฝงเป็นremovedKeyและละเว้น จะกลายเป็นnewObj { 2: 2, 3: 3, 4: 4 }โปรดทราบว่าไม่มีคีย์ที่ถูกลบออกค่าไม่ได้ถูกตั้งค่าเป็นundefinedเท่านั้น


ทางออกที่ดี ฉันมีกรณีการใช้งานที่คล้ายกัน (คีย์แบบไดนามิกในตัวลด)
ericgio


3

Lodash ละเว้น

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

ใช่ Lodash เป็นตัวเลือกที่สวยงาม แต่ฉันพยายามที่จะบรรลุเป้าหมายเดียวกันกับวานิลลา ES6
andreasonny83

3

คุณยังสามารถใช้ตัวดำเนินการแพร่กระจายเพื่อทำสิ่งนี้

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

2
ดูเหมือนว่าเก๋สุด ๆ สิ่งนี้ทำให้คีย์เป็นไม่ได้กำหนดในcopy
kano

เพื่อให้คุณสามารถลบคีย์ที่ไม่ได้กำหนดหากมีเพียงคีย์เดียวเท่านั้นที่จะอยู่ในนั้น
Victor

1
ไม่แน่ใจว่าทำไมคุณถึงเพิ่มส่วนขยายในสำเนาให้ const copy = { ...source, b: undefined } เดือดลงไปเหมือนกันทุกประการ
bert bruynooghe

3

การแก้ปัญหาข้างต้นโดยใช้โครงสร้างจะประสบกับความจริงที่ว่าคุณมีตัวแปรที่ใช้แล้วซึ่งอาจทำให้เกิดการร้องเรียนจาก ESLint หากคุณใช้สิ่งนั้น

ดังนั้นนี่คือคำตอบของฉัน:

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

บนแพลตฟอร์มส่วนใหญ่ (ยกเว้น IE ยกเว้นการใช้ Babel) คุณสามารถทำได้:

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


2

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

การวนรอบอย่างง่ายพร้อมกับการตรวจสอบ hasOwnProperty ควรใช้งานได้และสามารถปรับให้เข้ากับความต้องการในอนาคตได้มากขึ้น:

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

1
โซลูชันตัวดำเนินการสเปรดจะทำเช่นเดียวกันกับไวยากรณ์ที่ดีกว่า
david.pfx

2

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

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

ฉันทำแบบนี้เป็นตัวอย่างจาก Redux reducer ของฉัน:

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

ในคำอื่น ๆ :

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

ดูเหมือนว่าจะมีงานพิเศษมากมายเมื่อเทียบกับคำตอบที่ได้รับการยอมรับเมื่อ 3 ปีที่แล้ว
carpiediem

ฉันมีกรณีการใช้งานที่คล้ายกัน (ลด) ดังนั้นฉันมาด้วยวิธีที่ดีที่รองรับปุ่มแบบไดนามิกโดยไม่ต้องกลายพันธุ์ โดยทั่วไป: const { [removeMe]: removedKey, ...newObj } = obj;- ดูคำตอบของฉันสำหรับคำถามนี้
goldins

1

ฉันเพิ่งทำมันเป็นวิธีที่ง่ายมาก:

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

เพียงใช้ตัวดำเนินการสเปรดเพื่อแยกคุณสมบัติที่ไม่ต้องการ:

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

... และobject.assignเฉพาะส่วนที่เหลือ:

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

3
restเป็นวัตถุใหม่อยู่แล้ว - คุณไม่ต้องการบรรทัดสุดท้าย นอกจากนี้ยังเหมือนกับโซลูชันที่ยอมรับได้
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
ในขณะที่รหัสนี้อาจให้คำตอบสำหรับคำถาม แต่ก็ควรเพิ่มบริบทว่าทำไมมันถึงทำงานอย่างไร สิ่งนี้สามารถช่วยผู้ใช้ในอนาคตเรียนรู้และนำความรู้นั้นไปใช้กับรหัสของตนเอง คุณมีแนวโน้มที่จะได้รับการตอบรับเชิงบวกจากผู้ใช้ในรูปแบบของ upvotes เมื่ออธิบายรหัส
borchvm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.