ES6 деструктуризация
Разрушающий синтаксис позволяет деструктурировать и рекомбинировать объект либо с параметрами функции, либо с переменными.
Ограничение состоит в том, что список ключей предопределен, их нельзя перечислять в виде строк, как упоминается в вопросе. Разрушение становится более сложным, если ключ не алфавитно-цифровой, например foo_bar.
Недостатком является то, что для этого требуется продублировать список ключей, это приводит к подробному коду в случае, если список длинный. Поскольку в этом случае деструктурирование дублирует синтаксис литерала объекта, список можно скопировать и вставить как есть.
Плюс в том, что это эффективное решение, естественное для ES6.
IIFE
let subset = (({ foo, bar }) => ({ foo, bar }))(obj); // dupe ({ foo, bar })
Временные переменные
let { foo, bar } = obj;
let subset = { foo, bar }; // dupe { foo, bar }
Список строк
Произвольный список выбранных ключей состоит из строк, как того требует вопрос. Это позволяет не предопределять их и использовать переменные, которые содержат имена ключей, например pick(obj, 'foo', someKey, ...moreKeys).
Однострочник становится короче с каждым выпуском JS.
ES5
var subset = Object.keys(obj)
.filter(function (key) {
return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
ES6
let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => Object.assign(obj2, { [key]: obj[key] }), {});
Или с запятой оператор:
let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});
ES2019
ECMAScript 2017 имеет Object.entriesи Array.prototype.includes, ECMAScript 2019 имеет Object.fromEntries, они могут быть заполнены при необходимости и сделать задачу проще:
let subset = Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
Однострочникpick может быть переписан как вспомогательная функция, аналогичная Lodash, или omitгде список ключей передается через аргументы:
let pick = (obj, ...keys) => Object.fromEntries(
Object.entries(obj)
.filter(([key]) => keys.includes(key))
);
let subset = pick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1 }
Примечание о пропавших ключах
Основное различие между деструктуризацией и обычной Lodash-подобной pickфункцией заключается в том, что деструктуризация включает в себя несуществующие выбранные ключи со undefinedзначением в подмножестве:
(({ foo, bar }) => ({ foo, bar }))({ foo: 1 }) // { foo: 1, bar: undefined }
Такое поведение может быть или не быть желательным. Его нельзя изменить для деструктурирования синтаксиса.
Хотя pickможно изменить, чтобы включить отсутствующие ключи, выполнив итерацию списка выбранных ключей:
let inclusivePick = (obj, ...keys) => Object.fromEntries(
keys.map(key => [key, obj[key]])
);
let subset = inclusivePick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1, bar: undefined }