หนึ่งซับเพื่อนำคุณสมบัติบางอย่างจากวัตถุใน ES 6


153

วิธีหนึ่งสามารถเขียนฟังก์ชั่นซึ่งใช้เพียงไม่กี่คุณลักษณะในวิธีกะทัดรัดที่สุดใน ES6?

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

มีวิธีการที่บางกว่าหรือไม่?

(v) => {
    let { id, title } = v;
    return { id, title };
}

คำตอบ:


124

นี่คือบางสิ่งที่บางกว่าแม้ว่าจะไม่หลีกเลี่ยงการทำซ้ำรายการของฟิลด์ มันใช้ "การทำลายโครงสร้างพารามิเตอร์" เพื่อหลีกเลี่ยงความต้องการสำหรับvพารามิเตอร์

({id, title}) => ({id, title})

(ดูตัวอย่างที่รันได้ในคำตอบอื่น ๆนี้)

@ โซลูชันของ EthanBrown เป็นเรื่องทั่วไปมากขึ้น นี่คือเวอร์ชันที่ใช้สำนวนObject.assignและคุณสมบัติที่คำนวณได้มากกว่า ( [p]ส่วน):

function pick(o, ...props) {
    return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

ถ้าเราต้องการที่จะรักษาคุณสมบัติของคุณสมบัติเช่นconfigurableและ getters และ setters ในขณะที่ยังละเว้นคุณสมบัติที่ไม่นับได้แล้ว:

function pick(o, ...props) {
    var has = p => o.propertyIsEnumerable(p),
        get = p => Object.getOwnPropertyDescriptor(o, p);

    return Object.defineProperties({},
        Object.assign({}, ...props
            .filter(prop => has(prop))
            .map(prop => ({prop: get(props)})))
    );
}

10
+1 คำตอบที่ดี Torazaburo; ขอบคุณที่ทำให้ฉันตระหนักถึงObject.assign; es6 เหมือนต้นคริสต์มาสที่มีของขวัญมากมายอยู่ข้างใต้ฉันยังคงหาของขวัญได้หลายเดือนหลังจากวันหยุด
Ethan Brown

มีข้อผิดพลาด: คำอธิบายคุณสมบัติต้องเป็นวัตถุ: ไม่ได้กำหนด ไม่ควรfilter(...).map(prop => ({[prop]: get(prop)})))หรือ
ไม่มีที่สิ้นสุด

สำหรับpick()การใช้งานครั้งแรกของคุณคุณสามารถทำสิ่งที่ต้องการได้เช่นกันreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts

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

เมื่อไม่มีคุณสมบัติอยู่ในวัตถุคุณจะได้รับ undefinedเมื่อทรัพย์สินที่ไม่อยู่ในวัตถุที่คุณจะได้รับบางครั้งมันก็สำคัญ นอกจากนั้นเป็นความคิดที่ดี
x-yuri

43

ฉันไม่คิดว่ามีวิธีใดที่จะทำให้มันมากขึ้นขนาดกะทัดรัดกว่าคำตอบของคุณ (หรือ torazburo) แต่หลักสิ่งที่คุณกำลังพยายามที่จะทำคือการเลียนแบบเน้นของpickการดำเนินงาน มันจะง่ายพอที่จะนำไปใช้ใหม่ใน ES6:

function pick(o, ...fields) {
    return fields.reduce((a, x) => {
        if(o.hasOwnProperty(x)) a[x] = o[x];
        return a;
    }, {});
}

จากนั้นคุณมีฟังก์ชั่นที่ใช้งานได้สะดวก:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');

ขอบคุณ นี่ไม่ใช่คำตอบสำหรับคำถามของฉัน แต่เป็นการเพิ่มที่ดีมาก
kirilloid

7
(ยักไหล่) ฉันรู้สึกว่ามันเป็นคำตอบสำหรับการแก้ปัญหาของคุณ ไม่มีวิธีแก้ปัญหาทั่วไปที่บางกว่า(การแก้ปัญหาของ torazaburo จะลบออกจากขยะพิเศษ แต่ปัญหาสำคัญ - ที่ชื่อคุณสมบัติทั้งหมดจะต้องเขียนสองครั้ง - หมายความว่ามันไม่ได้ปรับขนาดได้ดีกว่าโซลูชันของคุณ) อย่างน้อยโซลูชันของฉันปรับขนาดได้ดี ... pickฟังก์ชั่นหนึ่งครั้งและคุณสามารถเลือกคุณสมบัติได้ตามต้องการและจะไม่เพิ่มเป็นสองเท่า
อีธานบราวน์

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

Bergi เป็นจุดที่สมเหตุสมผล ... ฉันเพิ่งพิจารณาคุณสมบัติ (ไม่ใช่วิธีการ) บนสายโซ่ต้นแบบเพื่อให้แปลกและ "ส่งกลิ่น" (ในขณะที่พวกเขาเป็นกลิ่นรหัส) และฉันชอบกรองตามค่าเริ่มต้น หากมีแอปพลิเคชันที่ต้องการคุณสมบัติต้นแบบดี ... อาจมีตัวเลือกสำหรับสิ่งนั้น
อีธานบราวน์

แล้วอาร์เรย์ json ล่ะ!
Rizwan Patel

19

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

การใช้Array#reduceหนึ่งสามารถเก็บแต่ละคีย์ที่จำเป็นบนวัตถุที่ว่างเปล่าซึ่งจะถูกส่งผ่านเป็นinitialValueฟังก์ชั่นสำหรับกล่าวว่า

ชอบมาก

const orig = {
  id: 123456789,
  name: 'test',
  description: '…',
  url: 'https://…',
};

const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});

console.log(filtered); // Object {id: 123456789, name: "test"}


11

วิธีที่สั้นกว่าเล็กน้อยโดยใช้เครื่องหมายจุลภาค:

const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})

console.log(
  pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)  


วิธีใช้ คุณสามารถยกตัวอย่างได้หรือไม่?
Tomas M

1
มันทำงานได้เช่นเดียวกับคนอื่น ๆpickฟังก์ชั่นในกระทู้นี้:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek

8

ข้อเสนอคุณสมบัติส่วนที่เหลือ / การแพร่กระจายวัตถุของ TC39จะทำให้เรียบเนียน:

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

(มันมีข้อเสียของการสร้างxและyตัวแปรที่คุณอาจไม่ต้องการ)


33
นี่เป็นรูปแบบที่สะดวกomitแต่ไม่ใช่pick
kirilloid

5
ฉันชอบที่จะเห็นความแตกต่างที่ไม่ตรงข้ามแน่นอนนี้เป็นข้อเสนอ ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam

3

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

const person = {
  fname: 'tom',
  lname: 'jerry',
  aage: 100,
}

let newPerson = {};

({fname: newPerson.fname, lname: newPerson.lname} = person);

console.log(newPerson);


(ดัชนี): 36 Uncaught SyntaxError: เป้าหมายการมอบหมายการทำลายโครงสร้างไม่ถูกต้อง
Remzes

@Remzes ไม่ทราบว่าคุณใช้งานที่ไหนและอย่างไร แต่มันทำงานได้ดีใน SO code editor และในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของ chrome
Saksham

ฉันใช้ jsfiddle
Remzes

ฉันได้ปรับปรุงคำตอบของคุณเล็กน้อย แต่ก็ยังละเอียดเกินไปเมื่อเทียบกับที่ OP ต้องการ มันไม่เพียง แต่ซ้ำชื่อเขต แต่ยังชื่อของวัตถุใหม่
Dan Dascalescu

3

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

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

2

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

const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});

console.log(picked);
// {id: "68646", title: "Scarface"}

น่าเสียดายที่ข้อเสนอดูเหมือนจะไม่ไปที่ใดก็ได้ในไม่ช้า แก้ไขล่าสุดในเดือนกรกฎาคม 2017 และยังคงเป็นฉบับร่างที่ด่าน 0 การแนะนำผู้เขียนอาจทิ้งหรือลืมไป

ES5 และรุ่นก่อนหน้า (โหมดไม่เข้มงวด)

ชวเลขที่สั้นที่สุดที่เป็นไปได้ที่ฉันสามารถนึกได้เกี่ยวกับคุณลักษณะภาษาโบราณที่ไม่มีใครใช้อีกต่อไป

Object.assign(target, {...(o => {
    with(o) return { id, title };
})(source)});

withข้อความต้องห้ามในโหมดเข้มงวดทำให้วิธีการนี้ไร้ประโยชน์ 99.999% ของ JavaScript ที่ทันสมัย ค่อนข้างน่าละอายเพราะนี่เป็นการใช้งานที่เหมาะสมเพียงครึ่งเดียวที่ฉันพบสำหรับwithคุณสมบัตินี้ 😀


1

ฉันมีวิธีการแก้ปัญหาคล้ายกับ Ethan Brown แต่มีpickฟังก์ชั่นที่สั้นลง ฟังก์ชั่นอื่นpick2จะใช้เวลานานกว่าเล็กน้อย (และช้ากว่า) แต่อนุญาตให้เปลี่ยนชื่อคุณสมบัติในลักษณะคล้ายกับ ES6

const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})

const pick2 = (o, ...props) => props.reduce((r, expr) => {
  const [p, np] = expr.split(":").map( e => e.trim() )
  return p in o ? {...r, [np || p]: o[p]} : r
}, {}) 

นี่คือตัวอย่างการใช้งาน:

const d = { a: "1", c: "2" }

console.log(pick(d, "a", "b", "c"))        // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }

1
เหตุผลในการลงคะแนนคืออะไร? มันไม่ทำงานสำหรับคุณ
Alexandr Priezzhev

0

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

function pick(o, ...props) {
  return Object.assign({}, ...props.map(prop => {
    if (o[prop]) return {[prop]: o[prop]};
  }));
}

// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }

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