Một lớp lót để lấy một số thuộc tính từ đối tượng trong ES 6


153

Làm thế nào người ta có thể viết một hàm, chỉ mất một vài thuộc tính theo cách gọn nhất trong ES6?

Tôi đã đưa ra giải pháp bằng cách sử dụng hàm hủy đối tượng + đơn giản hóa theo nghĩa đen, nhưng tôi không thích danh sách các trường được lặp lại trong mã.

Có một giải pháp thậm chí mỏng hơn?

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

Câu trả lời:


124

Đây là một cái gì đó mỏng hơn, mặc dù nó không tránh lặp lại danh sách các lĩnh vực. Nó sử dụng "hủy tham số" để tránh sự cần thiết của vtham số.

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

(Xem một ví dụ có thể chạy trong câu trả lời khác này ).

Giải pháp của @ EthanBrown là tổng quát hơn. Đây là một phiên bản thành ngữ hơn của nó sử dụng Object.assignvà các thuộc tính được tính toán ( [p]phần):

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

Nếu chúng ta muốn giữ các thuộc tính của thuộc tính, chẳng hạn như configurablevà getters và setters, trong khi cũng bỏ qua các thuộc tính không thể đếm được, thì:

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 câu trả lời hay, torazaburo; cảm ơn vì đã khiến tôi nhận ra Object.assign; es6 giống như một cây thông Giáng sinh với rất nhiều món quà bên dưới. Tôi vẫn đang tìm quà trong nhiều tháng sau kỳ nghỉ
Ethan Brown

Có một lỗi: Mô tả thuộc tính phải là một đối tượng: không xác định. Có nên không filter(...).map(prop => ({[prop]: get(prop)})))?
Vô tận

Để pick()thực hiện lần đầu tiên, bạn cũng có thể làm một cái gì đó nhưreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts

Thật không may, phiên bản chọn sẽ không phải là loại an toàn trong dòng chảy hoặc bản in. nếu bạn muốn loại an toàn, không có cách nào khác trong việc hủy cấu trúc gán đối tượng ban đầu, sau đó gán từng loại vào một đối tượng mới.
duhseekoh

Khi một tài sản không tồn tại trong một đối tượng, bạn nhận được undefined. Đôi khi nó quan trọng. Ngoài ra, ý tưởng tốt đẹp.
x-yuri

43

Tôi không nghĩ có cách nào để làm cho nó gọn hơn nhiều so với câu trả lời của bạn (hoặc torazburo), nhưng về cơ bản những gì bạn đang cố gắng làm là mô phỏng hoạt động của Underscorepick . Nó sẽ đủ dễ dàng để thực hiện lại điều đó trong ES6:

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

Sau đó, bạn có một chức năng tái sử dụng tiện dụng:

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

Cảm ơn. Đây không phải là một câu trả lời cho câu hỏi của tôi, nhưng bổ sung rất tốt đẹp.
kirilloid

7
(nhún vai) Tôi cảm thấy như đó một câu trả lời cho giải pháp của bạn; không có giải pháp chung mỏng hơn (giải pháp của torazaburo loại bỏ khỏi phần bổ sung, nhưng vấn đề thiết yếu - rằng tất cả các tên thuộc tính phải được viết hai lần - có nghĩa là nó không mở rộng hơn giải pháp của bạn). Giải pháp của tôi ít nhất là chia tỷ lệ tốt ... đúng pickchức năng một lần và bạn có thể chọn bao nhiêu thuộc tính bạn muốn và nó sẽ không nhân đôi chúng.
Ethan Brown

1
Tại sao bạn sử dụng hasOwnProperty? Nếu các trường được chọn bằng tay, thậm chí incó vẻ phù hợp hơn; mặc dù tôi sẽ bỏ qua việc kiểm tra hoàn toàn và chỉ để chúng mặc định undefined.
Bergi

Bergi, đó là một điểm hợp lý ... Tôi chỉ coi các thuộc tính (không phải phương thức) trên chuỗi nguyên mẫu là lạ và "có mùi" (vì chúng là mùi mã), và tôi thích lọc chúng theo mặc định. Nếu có một ứng dụng cần các thuộc tính nguyên mẫu, thì ... có thể có một tùy chọn cho điều đó.
Ethan Brown

những gì về mảng json!
Rizwan Patel

19

Mẹo để giải quyết vấn đề này như một lớp lót là lật phương pháp đã thực hiện: Thay vì bắt đầu từ đối tượng ban đầu orig, người ta có thể bắt đầu từ các phím họ muốn trích xuất.

Array#reduceSau đó, sử dụng một khóa có thể lưu trữ từng khóa cần thiết trên đối tượng trống được truyền vào dưới dạng initialValuehàm đã nói.

Thích như vậy:

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

Một giải pháp ngắn hơn một chút bằng cách sử dụng toán tử dấu phẩy:

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

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


Làm thế nào để sử dụng này? bạn có thể cung cấp một ví dụ?
Tomas M

1
Nó hoạt động giống như các pickchức năng khác trong chuỗi này:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek

8

Đề xuất thuộc tính nghỉ ngơi / trải rộng đối tượng của TC39 sẽ làm cho điều này trở nên thú vị:

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

(Nó có nhược điểm là tạo xycác biến mà bạn có thể không cần.)


33
Đây là một hình thức thuận tiện omit, nhưng khôngpick
kirilloid

5
Tôi rất thích thấy một biến thể hoàn toàn trái ngược với điều này như một đề xuất ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam

3

Bạn có thể sử dụng hàm hủy đối tượng để giải nén các thuộc tính khỏi đối tượng hiện có và gán chúng cho các biến có tên khác nhau - các trường của một đối tượng mới, ban đầu trống.

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

let newPerson = {};

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

console.log(newPerson);


(index): 36 Uncaught SyntaxError: Mục tiêu chuyển nhượng hủy hoại không hợp lệ
Remols

@Remze không biết bạn đang thực hiện việc này ở đâu và như thế nào nhưng nó hoạt động tốt trong trình chỉnh sửa mã SO và trong các công cụ phát triển chrome.
Saksham

Tôi đã sử dụng jsfiddle
Remols

Tôi đã cải thiện câu trả lời của bạn một chút, nhưng nó vẫn quá dài dòng, so với những gì OP yêu cầu. Nó lặp lại không chỉ tên trường, mà cả tên của đối tượng mới.
Dan Dascalescu

3

ES6 là thông số kỹ thuật mới nhất tại thời điểm câu hỏi được viết. Như đã giải thích trong câu trả lời này , việc chọn khóa ngắn hơn đáng kể trong ES2019 so với ES6:

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

2

Hiện tại có một đề xuất rơm để cải thiện cú pháp tốc ký đối tượng của JavaScript, cho phép "chọn" các thuộc tính được đặt tên mà không cần lặp lại:

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

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

Thật không may, đề xuất dường như không đi đến bất cứ lúc nào sớm. Chỉnh sửa lần cuối vào tháng 7 năm 2017 và vẫn là bản nháp ở Giai đoạn 0 , cho thấy tác giả có thể đã bỏ hoặc quên nó.

ES5 trở về trước (chế độ không nghiêm ngặt)

Tốc ký ngắn gọn nhất có thể mà tôi có thể nghĩ đến liên quan đến một tính năng ngôn ngữ cổ không ai sử dụng nữa:

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

withcác câu lệnh bị cấm trong chế độ nghiêm ngặt, làm cho cách tiếp cận này trở nên vô dụng đối với 99,999% JavaScript hiện đại. Hơi xấu hổ, vì đây là lần sử dụng nửa chừng duy nhất tôi tìm thấy cho withtính năng này. 😀


1

Tôi có giải pháp tương tự Ethan Brown, nhưng thậm chí còn ngắn hơn - pickchức năng. Một chức năng khác pick2dài hơn một chút (và chậm hơn), nhưng cho phép đổi tên các thuộc tính theo cách tương tự như 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
}, {}) 

Dưới đây là ví dụ sử dụng:

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
Lý do của downvote là gì? Nó không làm việc cho bạn?
Alexandr Priezzhev

0

Tôi yêu cầu giải pháp này nhưng tôi không biết liệu các khóa được đề xuất có sẵn không. Vì vậy, tôi đã lấy @torazaburo trả lời và cải thiện cho trường hợp sử dụng của mình:

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

0

lấy cảm hứng từ cách tiếp cận rút gọn của https://stackoverflow.com/users/865693/shesek :

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

sử dụng:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') kết quả trong: {model: "F40", productionYear: 1987}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.