วิธีรับชุดย่อยของคุณสมบัติของวัตถุจาวาสคริปต์


424

พูดว่าฉันมีวัตถุ:

elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};

ฉันต้องการสร้างวัตถุใหม่ด้วยชุดย่อยของคุณสมบัติ

 // pseudo code
 subset = elmo.slice('color', 'height')

 //=> { color: 'red', height: 'unknown' }

ฉันจะทำสิ่งนี้ได้อย่างไร


2
ไลบรารี Underscore มีฟังก์ชันผู้ช่วยจำนวนมากเช่นนี้ลองดู: underscorejs.org/#pick
Stefan

4
ฉันคิดว่าพูดemo.sliceอย่างรวดเร็วก่อน
Bill Criswell

1
ในความคิดที่สอง ... ฉันจะไม่สร้างเซ็ตย่อย ...
Andrew

คำตอบ:


673

การใช้การทำลายวัตถุและการจดชวเลขทรัพย์สิน

const object = { a: 5, b: 6, c: 7  };
const picked = (({ a, c }) => ({ a, c }))(object);

console.log(picked); // { a: 5, c: 7 }


จาก Philipp Kewisch:

นี่เป็นเพียงฟังก์ชั่นนิรนามที่ถูกเรียกทันที ทั้งหมดนี้สามารถพบได้ในหน้าการทำลายล้างใน MDN นี่คือรูปแบบขยาย

let unwrap = ({a, c}) => ({a, c});

let unwrap2 = function({a, c}) { return { a, c }; };

let picked = unwrap({ a: 5, b: 6, c: 7 });

let picked2 = unwrap2({a: 5, b: 6, c: 7})

console.log(picked)
console.log(picked2)


37
คุณเรียนรู้วิธีการทำสิ่งนี้ได้อย่างไร ไม่มีที่ไหนเลยในเอกสารหรือบทความที่ฉันเคยเห็น (รวมถึง MDN) แสดงให้เห็นถึงรูปแบบของลูกศรที่ใช้ในการทำลายวัตถุ นี่เป็นเรื่องดีที่จะรู้
papiro

52
นี่เป็นเพียงฟังก์ชั่นนิรนามที่ถูกเรียกทันที ทั้งหมดนี้สามารถพบได้ในหน้าการทำลายล้างใน MDN นี่คือรูปแบบขยาย: let unwrap = ({a, c}) => ({a, c}); let unwrap2 = function({a, c}) { return { a, c }; }; let picked = unwrap({ a: 5, b: 6, c: 7 });
Philipp Kewisch

8
มีวิธีการทำกับผู้ดำเนินการกระจายแบบไดนามิกหรือไม่?
Tom Sarduy

4
@TomSarduy คุณสามารถใช้ส่วนที่เหลือถ้าคุณต้องการที่จะระบุว่าอุปกรณ์ประกอบฉากที่จะลบเช่นconst { b, ...picked } = objectจะสร้างเป็นpicked { a: 5, c: 7 }คุณได้ระบุเพียงเพื่อลบข eslint ของคุณอาจจะรำคาญที่คุณประกาศ var ที่คุณไม่ได้ใช้
Josh จาก Qaribou

24
ข้อเสียที่นี่คือคุณต้องพิมพ์ชื่อชุดของแอตทริบิวต์สองครั้ง อาจเป็นปัญหาในกรณีที่จำเป็นต้องเลือกคุณลักษณะหลายอย่าง
Gershom

144

ฉันแนะนำให้ดูที่Lodash ; มันมีฟังก์ชั่นยูทิลิตี้ที่ยอดเยี่ยมมากมาย

ตัวอย่างเช่นpick()สิ่งที่คุณต้องการ:

var subset = _.pick(elmo, ['color', 'height']);

ซอ


2
เดียวกันสำหรับ underscore.js
Dan

1
มีฟังก์ชั่นที่จะยกเว้นเฉพาะบางฟิลด์แทนที่จะเลือกหรือไม่? ดังนั้นฉันมีเขตข้อมูลประมาณ 50 ใน json ของฉันและต้องการทุกอย่างยกเว้นเพียง 2 เขตข้อมูล
Shrikant Prabhu

7
อ๋อ! คุณสามารถใช้_.omit(elmo, ['voice'])เพื่อคืนค่าทุกอย่าง แต่voice
xavdid

สิ่งที่ฉันไม่ชอบเกี่ยวกับวิธีการนี้คือคุณกำลังใส่ชื่อฟิลด์ในเครื่องหมายอัญประกาศดังนั้นจึงมีความอ่อนไหวต่อการพิมพ์ผิด refactorings ทั่วไปเช่นการเปลี่ยนชื่อคุณสมบัติใน IDE ของคุณจะไม่มารับ ฯลฯ เป็นต้น
แอนดี้

117

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

const object = {
  a: 'a',
  b: 'b',
  c: 'c',
  d: 'd',
}

// Remove "c" and "d" fields from original object:
const {c, d, ...partialObject} = object;
const subset = {c, d};

console.log(partialObject) // => { a: 'a', b: 'b'}
console.log(subset) // => { c: 'c', d: 'd'};

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

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

1
นี้เป็นหลักเช่นเดียวกับสิ่งที่อยู่ในเป็นคำตอบของ @Ivan Nosovแม้ว่ามันอธิบายในทางที่เข้าใจมากขึ้นที่นี่
icc97

81

ในขณะที่มันมากขึ้นอีกนิด verbose คุณสามารถบรรลุสิ่งที่คนอื่น ๆ ได้รับการแนะนำขีด / lodash สำหรับ 2 ปีที่ผ่านมาโดยใช้Array.prototype.reduce

var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});

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

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

แก้ไข: รุ่น es7 เดียวกัน:

const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});

แก้ไข: ตัวอย่างที่ดีสำหรับการแกงด้วย! มีฟังก์ชั่น 'เลือก' ส่งคืนฟังก์ชันอื่น

const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});

ข้างต้นค่อนข้างใกล้เคียงกับวิธีอื่น ๆ ยกเว้นจะช่วยให้คุณสร้าง 'ตัวเลือก' ได้ทันที เช่น

pick('color', 'height')(elmo);

สิ่งที่ประณีตเป็นพิเศษเกี่ยวกับวิธีการนี้คือคุณสามารถผ่าน 'การเลือก' ที่เลือกไปยังสิ่งที่ใช้ฟังก์ชั่นได้อย่างง่ายดายเช่นArray#map:

[elmo, grover, bigBird].map(pick('color', 'height'));
// [
//   { color: 'red', height: 'short' },
//   { color: 'blue', height: 'medium' },
//   { color: 'yellow', height: 'tall' },
// ]

3
es6 ทำให้มันเป็นไปได้ที่จะทำความสะอาดผ่านฟังก์ชั่นลูกศรและการส่งคืนของ Object.assign (ตั้งแต่การกำหนดให้กับคุณสมบัติวัตถุส่งคืนค่าคุณสมบัติ แต่ Object.assign จะส่งคืนวัตถุ)
Josh จาก Qaribou

หมายเหตุอื่น ๆ ของ es6: คุณแทบจะไม่ต้องทำสิ่งนี้อีกเลยเพราะโดยทั่วไปแล้วคุณเพียงแค่ทำการกำหนดหรือมอบหมายงาน เช่นfunction showToy({ color, height }) {จะใส่เฉพาะสิ่งที่คุณต้องการในขอบเขต reduceวิธีการส่วนใหญ่ทำให้รู้สึกเมื่อคุณกำลังลดความซับซ้อนของวัตถุสำหรับการอนุกรม
Josh จาก Qaribou

6
ES6 เวอร์ชั่นนั้นมีประสิทธิภาพน้อยกว่าเพราะมันจะทำสำเนาคุณสมบัติทั้งหมดพร้อมกับการทำซ้ำแต่ละครั้ง มันทำให้การดำเนินงาน O (n) เป็น O (n ^ 2) ES6 ที่เทียบเท่ากับการบล็อกโค้ดครั้งแรกของคุณคือconst pick = (obj, props) => props.reduce((a, e) => (a[e] = obj[e], a), {});
4castle

@ 4castle yep การโทรที่ดี - ไม่รู้สึกซ้ำแล้วซ้ำอีก ฉันชอบไวยากรณ์ของเครื่องหมายจุลภาค - ดีกว่าผลตอบแทนที่ได้รับ
Josh จาก Qaribou

1
@ShevchenkoViktor ฉันจะใช้วิธีการนั้นใน es6 เวอร์ชั่นดั้งเดิมของฉัน แต่เปลี่ยนไปหลังจากความคิดเห็นของ @ 4castle ฉันคิดว่าสเปรดมีความชัดเจนมากขึ้น แต่มันมีความแตกต่างอย่างมากสำหรับวัตถุขนาดใหญ่ในโค้ดที่อาจอยู่ในคอขวดได้ง่าย (เช่นการล่าช้าในการแสดงผลข้อมูลที่ส่งคืนจากfetch) ดังนั้นฉันขอแนะนำให้เพิ่มความคิดเห็น
Josh จาก Qaribou

45

การทำลายล้าง ES6

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

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

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

ข้อเสียคือโซลูชันของนักแสดงที่เป็นธรรมชาติของ ES6

IIFE

let subset = (({ foo, bar }) => ({ foo, bar }))(obj); // dupe ({ foo, bar })

ตัวแปรชั่วคราว

let { foo, bar } = obj;
let subset = { foo, bar }; // dupe { foo, bar }

รายการของสตริง

รายการคีย์ที่เลือกโดยพลการประกอบด้วยสตริงตามที่คำถามต้องการ สิ่งนี้อนุญาตให้ไม่กำหนดค่าล่วงหน้าและใช้ตัวแปรที่มีชื่อคีย์เช่นpick(obj, 'foo', someKey, ...moreKeys)พวกเขาและใช้ตัวแปรที่มีชื่อที่สำคัญเช่น

สายการบินหนึ่งจะสั้นลงเมื่อใช้รุ่น JS

ES5

var subset = Object.keys(obj)
.filter(function (key) { 
  return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
  obj2[key] = obj[key];
  return obj2;
}, {});

ES6

let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => Object.assign(obj2, { [key]: obj[key] }), {});

หรือด้วยเครื่องหมายจุลภาค:

let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});

ES2019

ECMAScript 2017 มีObject.entriesและArray.prototype.includesECMAScript 2019 มีObject.fromEntriesพวกเขาสามารถ polyfilled เมื่อจำเป็นและทำให้งานง่ายขึ้น:

let subset = Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)

หนึ่งซับสามารถเขียนใหม่เป็นฟังก์ชันตัวช่วยคล้ายกับLodashpickหรือomitตำแหน่งของคีย์ถูกส่งผ่านอาร์กิวเมนต์:

let pick = (obj, ...keys) => Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => keys.includes(key))
);

let subset = pick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1 }

หมายเหตุเกี่ยวกับกุญแจที่หายไป

ความแตกต่างที่สำคัญระหว่างpickฟังก์ชั่นการทำลายล้างและการทำงานเหมือน Lodash ทั่วไปคือการทำลายล้างนั้นรวมถึงปุ่มเลือกที่ไม่มีอยู่จริงพร้อมundefinedค่าในชุดย่อย:

(({ foo, bar }) => ({ foo, bar }))({ foo: 1 }) // { foo: 1, bar: undefined }

พฤติกรรมนี้อาจเป็นที่ต้องการหรือไม่ก็ได้ ไม่สามารถเปลี่ยนแปลงได้สำหรับการทำลายไวยากรณ์

ในขณะที่pickสามารถเปลี่ยนเป็นรวมคีย์ที่หายไปได้โดยทำซ้ำรายการของคีย์ที่เลือกแทน:

let inclusivePick = (obj, ...keys) => Object.fromEntries(
  keys.map(key => [key, obj[key]])
);

let subset = inclusivePick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1, bar: undefined }

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

ES5 - "การอ้างอิง UncaughtError: obj ไม่ได้ถูกกำหนด"
Nikhil Vartak

@NikhilVartak คาดว่าobjตัวแปรนั้นเป็นตัวแปรที่เก็บวัตถุ objหากแตกต่างชื่อตัวแปรของคุณใช้มันแทน
Estus Flask

ความผิดฉันเอง! Object.keys(obj)ผมมองข้าม ง่วงนอน
Nikhil Vartak

34

ไม่มีอะไรเหมือนที่มีอยู่แล้วในไลบรารีหลัก แต่คุณสามารถใช้การทำลายวัตถุเพื่อทำ ...

const {color, height} = sourceObject;
const newObject = {color, height};

คุณสามารถเขียนฟังก์ชั่นยูทิลิตี้ทำมัน ...

const cloneAndPluck = function(sourceObject, keys) {
    const newObject = {};
    keys.forEach((obj, key) => { newObject[key] = sourceObject[key]; });
    return newObject;
};

const subset = cloneAndPluck(elmo, ["color", "height"]);

ห้องสมุดเช่น Lodash _.pick()ยังมี


1
เยี่ยมมากฉันต้องเปลี่ยน forEach เป็น: keys.forEach (key => {newObject [key] = sourceObject [key];}) ;. โปรดอัปเดตความคิดเห็นหากนี่สมเหตุสมผล
กานดา

34

Comma operatorผมเพิ่มคำตอบนี้เพราะไม่มีคำตอบที่ใช้

มันง่ายมากกับdestructuring assignmentและ,ผู้ประกอบการ

const object = { a: 5, b: 6, c: 7  };
const picked = ({a,c} = object, {a,c})

console.log(picked);

ด้วยการปรับปรุงบางอย่างสำหรับการกำหนดคุณสมบัติแบบไดนามิกมันอาจมีลักษณะเช่นนี้:


2
ไม่ดีของฉันฉันไม่รู้ว่าฉันทำอะไรมาก่อนว่ามันใช้งานไม่ได้ แต่ดูเหมือนว่ามันจะทำงาน
ekkis

1
นั่นคือการแสดงออกที่ดีที่สุดเมื่อพูดถึงการทำลายล้าง
Mohamed Allal

1
วิธีแก้ปัญหานี้ฉลาด แต่ไม่สามารถใช้งานได้ในโหมดเข้มงวด (เช่น'use strict') ReferenceError: a is not definedฉันได้รับ
kimbaudi

8
โปรดทราบว่าวิธีการนี้ทำให้ขอบเขตปัจจุบันมีตัวแปรสองตัวaและc- ระวังอย่าสุ่มเขียนทับ vars ในเครื่องหรือในระดับโลกโดยขึ้นอยู่กับบริบท (คำตอบที่ได้รับการยอมรับจะช่วยหลีกเลี่ยงปัญหานี้ได้โดยใช้ตัวแปรเฉพาะที่สองตัวในฟังก์ชั่นแบบอินไลน์ซึ่งอยู่นอกขอบเขตหลังจากการดำเนินการทันที)
mindplay.dk

2
มลภาวะในเนมสเปซทำให้สิ่งนี้ไม่สามารถทำได้ เป็นเรื่องธรรมดามากที่จะมีตัวแปรอยู่ในขอบเขตที่ตรงกับคุณสมบัติของวัตถุนั่นคือเหตุผลที่ว่ามีชอร์ต + การทำลายมีอยู่มากคุณอาจมีความสูงหรือสีที่กำหนดไว้แล้วในตัวอย่างดั้งเดิม
Josh จาก Qaribou

23

ทางออกหนึ่งเพิ่มเติม:

var subset = {
   color: elmo.color,
   height: elmo.height 
}

นี่ดูเหมือนฉันจะอ่านได้มากกว่าคำตอบใด ๆ ที่ค่อนข้างสวย แต่บางทีนั่นอาจเป็นฉัน!


9
ฉันชอบที่จะมีประสิทธิผลมากกว่าที่จะเป็นเรื่องแฟนซี แต่สับสนรหัสและในทางวิศวกรรมซอฟต์แวร์ในชีวิตจริงนี่เป็นทางออกที่อ่านได้ง่ายและบำรุงรักษาได้มากที่สุด
Janos

1
ใช่ แต่สำหรับฉันแล้วข้อดีอีกข้อหนึ่งของการใช้การทำลายล้างและการจดชวเลขคือว่ามีข้อผิดพลาดน้อยลง หากฉันมีเพนนีทุกครั้งที่ฉันคัดลอกและวางรหัสโดยไม่ตั้งใจเพื่อท้ายที่สุดsubset = {color: elmo.color, height: elmo.color}ฉันจะต้องมี ... อย่างน้อยอาจเป็นเล็กน้อย
JHH

ฉันจะไม่เรียกชวเลขในการทำลายล้างและข้อผิดพลาดน้อยลงเพราะมันไม่ใช่ DRY
gman

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

11

คุณสามารถใช้Lodash ได้เช่นกัน

var subset = _.pick(elmo ,'color', 'height');

สมมติว่าคุณมีอาร์เรย์ของ "elmo":

elmos = [{ 
      color: 'red',
      annoying: true,
      height: 'unknown',
      meta: { one: '1', two: '2'}
    },{ 
      color: 'blue',
      annoying: true,
      height: 'known',
      meta: { one: '1', two: '2'}
    },{ 
      color: 'yellow',
      annoying: false,
      height: 'unknown',
      meta: { one: '1', two: '2'}
    }
];

หากคุณต้องการพฤติกรรมเดียวกันโดยใช้ lodash คุณจะต้อง:

var subsets = _.map(elmos, function(elm) { return _.pick(elm, 'color', 'height'); });

10

การทำลายโครงสร้างลงในตัวแปรที่ตั้งชื่อแบบไดนามิกนั้นเป็นไปไม่ได้ใน JavaScript ตามที่กล่าวไว้ในคำถามนี้

ในการตั้งค่าปุ่มแบบไดนามิกคุณสามารถใช้ฟังก์ชั่นลดขนาดโดยไม่ต้องปิดวัตถุดังต่อไปนี้:

const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});

const elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
}

const subset = getSubset(elmo, 'color', 'annoying')
console.log(subset)

ควรทราบว่าคุณกำลังสร้างวัตถุใหม่ในการวนซ้ำทุกครั้งแทนการอัพเดตโคลนเดียว - mpen

ด้านล่างเป็นรุ่นที่ใช้การลดด้วยโคลนเดียว (อัปเดตค่าเริ่มต้นที่ส่งผ่านเพื่อลด)

const getSubset = (obj, ...keys) => keys.reduce((acc, curr) => {
  acc[curr] = obj[curr]
  return acc
}, {})

const elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
}

const subset = getSubset(elmo, 'annoying', 'height', 'meta')
console.log(subset)


1
! น่ากลัว มันทำให้ฉันชั่วขณะหนึ่งโดยไม่ทราบว่าความจำเป็นของวงเล็บปีกกาอยู่บน [c]:. ฉันสมมติว่านั่นทำให้ c ถูกมองว่าเป็นค่าแทนที่จะเป็นชื่อของคุณสมบัติ อย่างไรก็ตามเจ๋งมาก +1
John Fairbanks

ขอบคุณเพื่อน! การใช้งานนั้นเป็นสิ่งหนึ่งที่ฉันชอบเกี่ยวกับ JavaScript ซึ่งเปิดใช้งานฟังก์ชั่นทั่วไปโดยไม่ต้องใช้ eval สิ่งที่มันทำคือให้คุณตั้งค่าคีย์ของ dict ให้กับตัวแปรที่รันไทม์ หากคุณกำหนด var key = 'someKey' คุณสามารถใช้มันเป็น {[key]: 'value'} ซึ่งจะให้ {someKey: 'value'} เจ๋งจริงๆ
Muhammet Enginar

1
ควรทราบว่าคุณกำลังสร้างวัตถุใหม่ในการวนซ้ำทุกครั้งแทนการอัพเดตโคลนเดียว
mpen

1
@mpen หาดี ฉันได้เพิ่มรุ่นกลายพันธุ์โคลนเดียวตามที่คุณแนะนำยังกระจาย args แทนผ่านอาร์เรย์ของคีย์
Muhammet Enginar

7

โซลูชั่นของ TypeScript:

export function pick<T extends object, U extends keyof T>(obj: T, paths: Array<U>) {
    return paths.reduce((o, k) => {
        o[k] = obj[k];
        return o;
    }, Object.create(null));
}

ข้อมูลการพิมพ์ยังช่วยให้การเติมข้อความอัตโนมัติสมบูรณ์:

เครดิตให้กับDefinitelyTypedสำหรับU extends keyof Tเคล็ดลับ!


ไม่ทำงานสำหรับฉัน
Nuthinking

@ การตรวจสอบคุณใช้รหัส VS หรือไม่
mpen

ใช่ฉันใช้ VS Code
Nuthinking

@ คิดและคุณใส่มันในไฟล์. ts หรือเปล่า ว่าควรจะทำงาน ฉันก็พยายามอีกครั้ง
mpen


4

โซลูชั่นแบบไดนามิก

['color', 'height'].reduce((a,b) => (a[b]=elmo[b],a), {})


3

อีกวิธีหนึ่ง ...

var elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
}

var subset = [elmo].map(x => ({
  color: x.color,
  height: x.height
}))[0]

คุณสามารถใช้ฟังก์ชั่นนี้กับอาร์เรย์ของ Objects =)


2

เกี่ยวกับ:

function sliceObj(obj) {
  var o = {}
    , keys = [].slice.call(arguments, 1);
  for (var i=0; i<keys.length; i++) {
    if (keys[i] in obj) o[keys[i]] = obj[keys[i]];
  }
  return o;
}

var subset = sliceObj(elmo, 'color', 'height');

สิ่งนี้จะล้มเหลวหากค่าของคุณสมบัติเป็นfalse(หรือเป็นเท็จ) jsfiddle.net/nfAs8
Alxandr

keys[i] in objนั่นเป็นเหตุผลที่ผมเปลี่ยนไป
elclanrs

2

สิ่งนี้ใช้ได้กับฉันในคอนโซล Chrome มีปัญหาอะไรไหม?

var { color, height } = elmo
var subelmo = { color, height }
console.log(subelmo) // {color: "red", height: "unknown"}

4
สิ่งนี้อ่านได้ดี แต่สร้างตัวแปรที่ไม่จำเป็นสองสีและความสูง
user424174

อย่าเข้าใจความคิดเห็นของคุณ ความต้องการของ OP คือการสร้างวัตถุที่มีองค์ประกอบสองอย่าง
MSi

@MSi สิ่งนี้ไม่เพียงสร้างวัตถุเดียว แต่ยังสร้างสองตัวแปร
ออกแบบโดย Adrian

1

การทำลายโครงสร้างด้วยคุณสมบัติแบบไดนามิก

โซลูชันนี้ไม่เพียงใช้กับตัวอย่างเฉพาะของคุณเท่านั้น

const subset2 = (x, y) => ({[x]:a, [y]:b}) => ({[x]:a, [y]:b});

const subset3 = (x, y, z) => ({[x]:a, [y]:b, [z]:c}) => ({[x]:a, [y]:b, [z]:c});

// const subset4...etc.


const o = {a:1, b:2, c:3, d:4, e:5};


const pickBD = subset2("b", "d");
const pickACE = subset3("a", "c", "e");


console.log(
  pickBD(o), // {b:2, d:4}
  pickACE(o) // {a:1, c:3, e:5}
);

คุณสามารถกำหนดsubset4ฯลฯได้อย่างง่ายดายเพื่อพิจารณาคุณสมบัติเพิ่มเติม


1

ดีมากArray.prototype.reduce:

const selectable = {a: null, b: null};
const v = {a: true, b: 'yes', c: 4};

const r = Object.keys(selectable).reduce((a, b) => {
  return (a[b] = v[b]), a;
}, {});

console.log(r);

คำตอบนี้ใช้ตัวดำเนินการคอมม่าที่มีมนต์ขลังเช่นกัน: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

หากคุณต้องการได้รับแฟนซีจริง ๆ นี้มีขนาดกะทัดรัดมากขึ้น

const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});

นำทั้งหมดมารวมกันเป็นฟังก์ชั่นที่ใช้ซ้ำได้

const getSelectable = function (selectable, original) {
  return Object.keys(selectable).reduce((a, b) => (a[b] = original[b], a), {})
};

const r = getSelectable(selectable, v);
console.log(r);

1
  1. แปลงอาร์กิวเมนต์เป็นอาร์เรย์

  2. ใช้Array.forEach()เพื่อเลือกคุณสมบัติ

    Object.prototype.pick = function(...args) {
       var obj = {};
       args.forEach(k => obj[k] = this[k])
       return obj
    }
    var a = {0:"a",1:"b",2:"c"}
    var b = a.pick('1','2')  //output will be {1: "b", 2: "c"}

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

0
function splice()
{
    var ret = new Object();

    for(i = 1; i < arguments.length; i++)
        ret[arguments[i]] = arguments[0][arguments[i]];

    return ret;
}

var answer = splice(elmo, "color", "height");

0

ลอง

const elmo={color:"red",annoying:!0,height:"unknown",meta:{one:"1",two:"2"}};

const {color, height} = elmo; newObject = ({color, height});

console.log(newObject); //{ color: 'red', height: 'unknown' }

0

การเพิ่ม 2 เซนต์ของฉันไปยังคำตอบของIvan Nosov :

ในกรณีของฉันฉันต้องการปุ่มจำนวนมากที่จะ 'ตัด' ออกจากวัตถุดังนั้นมันจึงน่าเกลียดอย่างรวดเร็วและไม่ใช่วิธีแก้ปัญหาแบบไดนามิกมาก:

const object = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ddd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
const picked = (({ a, aa, aaa, ab, c, cc, ccc, cd }) => ({ a, aa, aaa, ab, c, cc, ccc, cd }))(object);

console.log(picked);

ดังนั้นนี่คือโซลูชันแบบไดนามิกโดยใช้ eval:

const slice = (k, o) => eval(`(${k} => ${k})(o)`);


const object    = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ddd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
const sliceKeys = '({ a, aa, aaa, ab, c, cc, ccc, cd })';

console.log( slice(sliceKeys, object) );

-2

หมายเหตุ: แม้ว่าคำถามเดิมที่ถามมาสำหรับจาวาสคริปต์ก็สามารถทำได้ jQuery โดยวิธีการแก้ปัญหาด้านล่าง

คุณสามารถขยาย jquery ถ้าคุณต้องการที่นี่เป็นตัวอย่างรหัสสำหรับหนึ่งชิ้น:

jQuery.extend({
  sliceMe: function(obj, str) {
      var returnJsonObj = null;
    $.each( obj, function(name, value){
        alert("name: "+name+", value: "+value);
        if(name==str){
            returnJsonObj = JSON.stringify("{"+name+":"+value+"}");
        }

    });
      return returnJsonObj;
  }
});

var elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};


var temp = $.sliceMe(elmo,"color");
alert(JSON.stringify(temp));

นี่คือซอเพื่อการเดียวกัน: http://jsfiddle.net/w633z/


16
jquery คืออะไร
Christian Schlensker

1
@ChristianSchlensker It's javascript
Kevin B
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.