Однострочник для получения некоторых свойств от объекта в ES 6

153

Как можно написать функцию, которая принимает только несколько атрибутов наиболее компактным способом в ES6?

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

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

(v) => {
    let { id, title } = v;
    return { id, title };
}
kirilloid
источник

Ответы:

124

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

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

(См. Работающий пример в этом другом ответе ).

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

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

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

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 хороший ответ, торазабуро; спасибо, что заставил меня осознать Object.assign; es6 похожа на рождественскую елку с таким количеством подарков, я все еще нахожу подарки спустя месяцы после праздника
Этан Браун
Получена ошибка: описание свойства должно быть объектом: undefined. Не должно ли это быть filter(...).map(prop => ({[prop]: get(prop)})))?
бесконечное
Для вашей первой pick()реализации вы также можете сделать что-то вродеreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Патрик Робертс
к сожалению, эта версия выбора не будет безопасна для типов в потоке или в машинописи. если вам нужна безопасность типов, нет способа обойти деструктурирование присвоения исходного объекта, затем присваивать каждый из них новому объекту.
duhseekoh
Когда свойство не существует в объекте, вы получаете undefined. Иногда это важно. Кроме этого, хорошая идея.
x-
43

Я не думаю , что есть какой - нибудь способ сделать это гораздо более компактным , чем ваш ответ (или torazburo), но в основном то , что вы пытаетесь сделать , это эмулировать Underscore в 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');
Этан Браун
источник
Спасибо. Это не ответ на мой вопрос, но очень приятное дополнение.
Кириллоид
7
(пожав плечами) Я чувствую , что это является ответом на ваше решение; нет более тонкого общего решения (решение torazaburo удаляет лишние лишние слова, но существенная проблема - все имена свойств должны быть записаны дважды - означает, что оно не масштабируется лучше, чем ваше решение). Мое решение, по крайней мере, хорошо масштабируется ... исправьте pickфункцию один раз, и вы можете выбрать столько свойств, сколько захотите, и они не удвоятся.
Итан Браун
1
Почему вы используете hasOwnProperty? Если поля выбраны вручную, даже inкажется более подходящим; хотя я бы пошел, чтобы полностью пропустить проверку и просто позволить им по умолчанию undefined.
Берги
Берги, это разумный момент ... Я просто считаю свойства (а не методы) в цепочке прототипов странными и "вонючими" (так как они представляют собой запах кода), и я предпочитаю отфильтровывать их по умолчанию. Если есть приложение, которое нуждается в свойствах прототипа, ну ... может быть вариант для этого.
Итан Браун
как насчет массивов JSON!
Ризван Патель
19

Хитрость для решения этой проблемы как однострочной заключается в том, чтобы изменить принятый подход: вместо того, чтобы начинать с исходного объекта orig , можно начать с ключей, которые они хотят извлечь.

С помощью Array#reduce one может затем сохранить каждый необходимый ключ на пустом объекте, который передается как 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"}

Bramus
источник
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')
)  

shesek
источник
как это использовать? Можете ли вы привести пример?
Томас М
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переменные , которые вы , возможно , не нужны.)

alxndr
источник
33
Это удобная форма omit, но неpick
кириллоид
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);

Saksham
источник
(индекс): 36 Uncaught SyntaxError: недопустимая цель назначения деструктуризации
Remzes
@Remzes не знает, где и как вы выполняете это, но это хорошо работает в редакторе кода SO и в инструментах разработчика Chrome.
Сакшам
Я использовал jsfiddle
Remzes
Я немного улучшил ваш ответ, но он все еще слишком многословен, по сравнению с тем, что попросил ОП. Он повторяет не только имена полей, но и имя нового объекта.
Дан Даскалеску
3

ES6 была последней спецификацией на момент написания вопроса. Как объясняется в этом ответе , выбор ключей в ES2019 значительно короче, чем в ES6:

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)
Настой Эстус
источник
2

В настоящее время существует специальное предложение по улучшению синтаксиса сокращений объектов 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

У меня есть аналогичное решение Итана Брауна, но еще короче - 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
В чем причина понижения голоса? Разве это не работает для вас?
Александр Приезжев
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

вдохновлен сокращенным подходом https://stackoverflow.com/users/865693/shesek :

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

использование:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') результаты в: {model: "F40", productionYear: 1987}

Кевин К.
источник