一行程序从 ES 6中的对象获取一些属性

如何编写一个在 ES6中以最紧凑的方式只需要很少属性的函数?

我已经想出了解决方案,使用解构 + 简化对象文字,但我不喜欢的字段列表是重复的代码。

还有更简单的解决办法吗?

(v) => {
let { id, title } = v;
return { id, title };
}
72645 次浏览

这里有一些更简单的方法,尽管它不会避免重复字段列表。它使用“参数解构”来避免需要 v参数。

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

(参见此 另一个答案中的一个可运行示例)。

@ Ethan Brown 的解决方案更为笼统。下面是一个更惯用的版本,它使用 Object.assign和计算属性([p]部分) :

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

如果我们想保留属性的属性,比如 configurable和 getter 和 setter,同时省略不可枚举的属性,那么:

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

我不认为有任何方法可以使它比你的答案(或 torazburo 的)更紧凑,但本质上你正在尝试做的是模仿 下划线的 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');

我有类似于伊森布朗的解决方案,但更短的 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" }

作为一行程序解决这个问题的诀窍在于改变所采取的方法: 不必从原始对象 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 filtered = ['id', 'name'].reduce((result, key) => ({
...result,
[key]: orig[key]
}), {});


console.log(filtered); // Object {id: 123456789, name: "test"}

TC39的 object rest/span properties 提案 会让这个过程变得相当圆滑:

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

(它确实有创建可能不需要的 xy变量的缺点。)

我需要这个解决方案,但我不知道建议的钥匙是否可用。因此,我选择了@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' }

使用逗号运算符的一个更简短的解决方案:

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


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

可以使用对象解构从现有对象和新的、最初为空的对象的 将它们分配给具有不同名称的变量字段中解压缩属性。

const 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年7月,至今仍是 第0阶段的草稿,这表明作者可能已经放弃或忘记了它。

ES5及更早版本(非严格模式)

我能想到的最简洁的速记方法是使用一个没有人再使用的 古老的语言特征:

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

在严格模式下禁止使用 with语句,这使得这种方法对于99.999% 的现代 JavaScript 来说都是无用的。有点遗憾,因为这是我为 with特性找到的唯一一个半成品的用法。

ES6是问题编写时的最新规范。正如在 这个答案中解释的那样,ES2019中的密钥拾取明显短于 ES6:

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

受到 https://stackoverflow.com/users/865693/shesek的减少方法的启发:

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

或者使用逗号运算符(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator)更短一些

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

用途:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') 结果: {model: "F40", productionYear: 1987}

上面提到了一些很棒的解决方案,但是没有看到一个关于 Typecript 的具体解决方案,所以就这样了。根据上面@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 omit,一个是更多的性能下面,但较少的 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;
};