Однострочник для получения некоторых свойств от объекта в ES 6
Как можно написать функцию, которая принимает только несколько атрибутов наиболее компактным способом в ES6?
Я придумал решение, использующее деструктурирование + упрощенный литерал объекта, но мне не нравится, что список полей повторяется в коде.
Есть ли еще более тонкое решение?
(v) => {
let { id, title } = v;
return { id, title };
}
13 ответов
Вот что-то более тонкое, хотя это не мешает повторять список полей. Он использует "деструктуризацию параметров", чтобы избежать необходимости 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)})))
);
}
Я не думаю, что есть какой-то способ сделать его намного более компактным, чем ваш ответ (или торазбюро), но, по сути, вы пытаетесь подражать 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');
Хитрость в решении этой проблемы как однострочника заключается в том, чтобы изменить принятый подход: вместо того, чтобы начинать с исходного объекта 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"}
Немного более короткое решение с использованием оператора запятой:
const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})
ES6 была последней спецификацией на момент написания вопроса. Как объясняется в этом ответе, в ES2019 выбор ключей значительно короче, чем в ES6:
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
Предложение свойств оставшихся / распространенных объектов TC39 сделает это довольно привлекательным:
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
z; // { a: 3, b: 4 }
(У него есть обратная сторона создания x
а также y
переменные, которые вам могут не понадобиться.)
Можно было бы использовать destructuring
назначить свойства пустому объекту.
let person = {
fname:'tom',
lname:'jerry',
aage:100
}
let newPerson = {};
({fname: newPerson.fname, lname: newPerson.lname} = person);
console.log(newPerson);
В настоящее время есть специальное предложение по улучшению синтаксиса сокращений объектов 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
характерная черта.
У меня есть подобное решение Итана Брауна, но еще короче - 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" }
Мне требовалось это решение, но я не знал, были ли предложенные ключи доступны. Итак, я взял ответ @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' }
Вот мое решение:
originalObject = {a:1, b:2, c:3, d:4, e:5}
picks = ["b", "d"]
Object.fromEntries(Object.entries(originalObject).filter(entry => picks.includes(entry[0])))
что приводит к
{b: 2, d: 4}
Несколько отличных решений выше, не видел ни одного для Typescript, так что вот оно. На основе решения @Ethan Browns выше
const pick = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Pick< T, K > =>
keys.reduce< any >( ( r, key ) => {
r[ key ] = obj[ key ];
return r;
}, {} );
И в качестве бонуса, вот дружественный к TS es6, и тот, который намного более производительен ниже, но меньше es6.
const omit = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Omit< T, K > =>
keys.reduce( ( r, key ) => ( delete r[ key ], r ), {
...obj,
} );
Более производительныйomit
: http://jsben.ch/g6QCK
const omit = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Omit< T, K > => {
let r: any = {};
let length = keys.length;
while ( length-- ) {
const key = keys[ length ];
r[ key ] = obj[ key ];
}
return r;
};
Вдохновлен сокращенным подходом 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}