One-liner untuk mengambil beberapa properti dari objek di ES 6

153

Bagaimana seseorang dapat menulis suatu fungsi, yang hanya membutuhkan beberapa atribut dengan cara yang paling ringkas di ES6?

Saya telah datang dengan solusi menggunakan destruct + objek yang disederhanakan literal, tapi saya tidak suka daftar bidang diulang dalam kode.

Apakah ada solusi yang lebih ramping?

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

Jawaban:

124

Ini adalah sesuatu yang lebih ramping, meskipun itu tidak menghindari mengulangi daftar bidang. Ia menggunakan "perusakan parameter" untuk menghindari kebutuhan akan vparameter.

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

(Lihat contoh runnable dalam jawaban lain ini ).

Solusi @ EthanBrown lebih umum. Berikut ini adalah versi yang lebih idiomatis yang menggunakan Object.assign, dan menghitung properti ( [p]bagian):

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

Jika kita ingin melestarikan atribut properti, seperti configurabledan getter dan setter, sementara juga menghilangkan properti yang tidak dapat dihitung, maka:

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)})))
    );
}
Dan Dascalescu
sumber
10
+1 jawaban yang bagus, torazaburo; terima kasih telah membuat saya sadar Object.assign; es6 seperti pohon Natal dengan begitu banyak hadiah di bawahnya aku masih menemukan hadiah berbulan-bulan setelah liburan
Ethan Brown
Mendapat kesalahan: Deskripsi properti harus berupa objek: tidak terdefinisi. Bukankah seharusnya begitu filter(...).map(prop => ({[prop]: get(prop)})))?
Tak Berujung
Untuk pick()implementasi pertama Anda, Anda juga bisa melakukan sesuatu sepertireturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts
sayangnya versi pick tidak akan mengetikkan aman dalam aliran atau naskah. jika Anda ingin mengetikkan keamanan, tidak ada jalan lain untuk merusak penetapan objek asli, kemudian menetapkan masing-masing ke dalam objek baru.
duhseekoh
Ketika properti tidak ada di suatu objek, Anda dapatkan undefined. Terkadang itu penting. Selain itu, ide bagus.
x-yuri
43

Saya tidak berpikir ada cara untuk membuatnya jauh lebih kompak daripada jawaban Anda (atau torazburo), tetapi pada dasarnya apa yang Anda coba lakukan adalah meniru operasi Underscorepick . Akan cukup mudah untuk mengimplementasikannya kembali di ES6:

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

Maka Anda memiliki fungsi berguna yang dapat digunakan kembali:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
sumber
Terima kasih. Ini bukan jawaban untuk pertanyaan saya, tetapi tambahan yang sangat bagus.
Kirilloid
7
(mengangkat bahu) Saya merasa itu adalah jawaban untuk solusi Anda; tidak ada solusi umum yang lebih ramping (solusi torazaburo menghilangkan dari ekstra vernak, tetapi masalah penting - bahwa semua nama properti harus ditulis dua kali - artinya tidak ada skala yang lebih baik daripada solusi Anda). Solusi saya setidaknya skala dengan baik ... benar pickfungsi sekali, dan Anda dapat memilih properti yang Anda inginkan dan tidak akan menggandakannya.
Ethan Brown
1
Mengapa Anda menggunakan hasOwnProperty? Jika bidang dipilih secara manual, bahkan intampaknya lebih sesuai; Meskipun saya akan pergi untuk menghilangkan cek sepenuhnya dan biarkan mereka default undefined.
Bergi
Bergi, ini adalah hal yang masuk akal ... Saya hanya menganggap properti (bukan metode) pada rantai prototipe menjadi aneh dan "bau" (karena mereka adalah bau kode), dan saya lebih memilih untuk menyaringnya secara default. Jika ada aplikasi yang membutuhkan properti prototipe, ya ... mungkin ada opsi untuk itu.
Ethan Brown
bagaimana dengan json array!
Rizwan Patel
19

Trik untuk memecahkan ini sebagai satu-liner adalah membalik pendekatan yang diambil: Daripada memulai dari objek asli orig, seseorang dapat mulai dari kunci yang ingin mereka ekstrak.

Menggunakan Array#reducesatu kemudian dapat menyimpan setiap kunci yang diperlukan pada objek kosong yang dilewatkan sebagai fungsi initialValueuntuk kata.

Seperti itu:

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

Bramus
sumber
11

Solusi sedikit lebih pendek menggunakan operator koma:

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

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

shesek
sumber
bagaimana cara menggunakan ini? Bisakah Anda memberikan contoh?
Tomas M
1
Ini berfungsi seperti pickfungsi lain di utas ini:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek
8

Proposal objek rest / spread properti TC39 akan membuat ini cukup apik:

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

(Ini memang memiliki kelemahan dalam membuat xdan yvariabel yang mungkin tidak Anda butuhkan.)

alxndr
sumber
33
Ini adalah bentuk yang nyaman omit, tetapi tidakpick
Kirilloid
5
Saya akan senang melihat varian yang melakukan kebalikan dari ini sebagai proposal ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam
3

Anda dapat menggunakan perusakan objek untuk mengekstrak properti dari objek yang ada dan menetapkannya ke variabel dengan nama yang berbeda - bidang objek yang awalnya kosong.

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

let newPerson = {};

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

console.log(newPerson);

Saksham
sumber
(indeks): 36 Sintaks Tanpa PerintahError: Target penugasan destruktif tidak valid
Remzes
@Remzes tidak tahu di mana dan bagaimana Anda menjalankan ini tetapi berfungsi dengan baik di editor kode SO dan di alat pengembang krom.
Saksham
Saya menggunakan jsfiddle
Remses
Saya sedikit memperbaiki jawaban Anda, tetapi masih terlalu bertele-tele, dibandingkan dengan apa yang diminta OP. Itu tidak hanya mengulangi nama bidang, tetapi juga nama objek baru.
Dan Dascalescu
3

ES6 adalah spesifikasi terbaru pada saat pertanyaan itu ditulis. Seperti dijelaskan dalam jawaban ini , pengambilan kunci secara signifikan lebih pendek di ES2019 daripada di ES6:

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

Saat ini ada proposal untuk meningkatkan sintaksis steno objek JavaScript, yang akan memungkinkan "memetik" properti bernama tanpa pengulangan:

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

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

Sayangnya, proposal tersebut sepertinya tidak akan kemana-mana dalam waktu dekat. Terakhir diedit pada bulan Juli 2017 dan masih konsep pada Tahap 0 , menunjukkan penulis mungkin telah membuang atau melupakannya.

ES5 dan sebelumnya (mode non-ketat)

Tulisan singkat yang mungkin saya pikirkan melibatkan fitur bahasa kuno yang tidak digunakan lagi oleh orang lain:

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

withpernyataan dilarang dalam mode ketat, membuat pendekatan ini tidak berguna untuk 99,999% dari JavaScript modern. Sedikit memalukan, karena ini adalah satu-satunya penggunaan yang layak untuk withfitur ini. 😀


sumber
1

Saya memiliki mirip dengan solusi Ethan Brown, tetapi bahkan lebih pendek - pickberfungsi. Fungsi lain pick2sedikit lebih lama (dan lebih lambat), tetapi memungkinkan untuk mengubah nama properti dengan cara yang mirip dengan 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
}, {}) 

Berikut ini contoh penggunaannya:

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" }
Alexandr Priezzhev
sumber
1
Apa alasan downvote? Apakah itu tidak berhasil untuk Anda?
Alexandr Priezzhev
0

Saya memerlukan solusi ini tetapi saya tidak tahu apakah kunci yang diusulkan tersedia. Jadi, saya mengambil jawaban @torazaburo dan ditingkatkan untuk kasus penggunaan saya:

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' }
Alwin Kesler
sumber
0

terinspirasi oleh pendekatan pengurangan https://stackoverflow.com/users/865693/shesek :

const pick = (orig, ...keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})

pemakaian:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') menghasilkan: {model: "F40", productionYear: 1987}

Kevin K.
sumber