104

Как написать функцию, которая берет только несколько атрибутов самым компактным способом в ES6?One-liner, чтобы взять некоторые свойства объекта в ES 6

Я придумал решение с использованием деструктурирования + упрощенного объектного литерала, но мне не нравится, что в коде повторяется список полей.

Есть ли еще более тонкое решение?

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

Я обнаружил, что хочу сделать именно это. –

ответ

84

Вот что-то более тонкое, хотя оно не позволяет повторять список полей. Он использует «деструктурирование параметров», чтобы избежать необходимости в параметре v.

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

@ Решение EthanBrown является более общим. Вот более идиоматическая версия его, которая использует Object.assign и вычисляемые свойства ([p] части):

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

Если мы хотим сохранить атрибуты от свойств, таких как configurable и добытчики и сеттера, а также пропуска не -enumerable свойства, затем:

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)}))) 
    ); 
} 
+6

+1 приятный ответ, торазабуро; спасибо за то, что вы узнали о «Object.assign»; es6 походит на рождественскую елку с таким количеством подарков под ней. Я все еще нахожу подарки через несколько месяцев после праздника. –

+0

Получена ошибка: Описание свойства должно быть объектом: undefined. Не должен быть 'filter (...). Map (prop => ({[prop]: get (prop)})))'? – Endless

+0

Для вашей первой реализации 'pick()' вы также можете сделать что-то вроде 'return props.reduce ((r, prop) => (r [prop] = o [prop], r), {})' –

32

Я не думаю, что есть какой-нибудь способ сделать это гораздо более компактным, чем ваш ответ (или torazburo), но в основном то, что вы пытаетесь сделать, это подражать Underscore's pick operation. Было бы достаточно легко повторно реализовать, что в 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'); 
+0

Спасибо. Это не ответ на мой вопрос, но очень приятное дополнение. – kirilloid

+4

(shrug) Я чувствую, что это _is_ ответ для вашего решения; нет более тонкого решения _general_ (решение torazaburo удаляется из дополнительного глагола, но основная проблема - все имена свойств должны быть записаны дважды - означает, что он не масштабируется лучше вашего решения). Мое решение, по крайней мере, хорошо масштабируется ... прямо после функции 'pick', и вы можете выбрать столько свойств, которые хотите, и они не будут удваивать их. –

+1

Почему вы используете 'hasOwnProperty'? Если поля выделены вручную, даже «in» представляется более подходящим; хотя я бы полностью отказался от проверки и просто дал им по умолчанию 'undefined'. – Bergi

0

У меня есть похожее на решение Этана Брауна, но еще короче - 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" } 
+0

В чем причина downvote? Разве это не работает для вас? –

12

Хитрости к решению этого как однострочник является переворачивать подход: Вместо того, начиная с исходного объектом 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"}

5

TC39's object rest/spread properties proposal сделает это довольно гладким:

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

(Он имеет оборотную сторону создания x и y переменные, которые вы можете не нужно.)

+18

Это удобная форма 'omit', но не' pick' – kirilloid

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

Крошечная немного короче решение, используя оператор запятая:

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

как это использовать? Можете ли вы привести пример? –

+0

Он работает так же, как и другие функции 'pick' в этой теме:' pick ({name: 'John', age: 29, height: 198}, 'name', 'age') ' – shesek

Смежные вопросы