说我有一个对象:
elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
我想使用其属性的子集创建一个新对象。
// pseudo code
subset = elmo.slice('color', 'height')
//=> { color: 'red', height: 'unknown' }
我怎样才能做到这一点?
说我有一个对象:
elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
我想使用其属性的子集创建一个新对象。
// pseudo code
subset = elmo.slice('color', 'height')
//=> { color: 'red', height: 'unknown' }
我怎样才能做到这一点?
使用对象解构和属性速记
const object = { a: 5, b: 6, c: 7 };
const picked = (({ a, c }) => ({ a, c }))(object);
console.log(picked); // { a: 5, c: 7 }
菲利普·凯维希 (Philipp Kewisch):
这实际上只是一个被立即调用的匿名函数。所有这些都可以在 MDN的Destructuring Assignment页面上找到。这是一个展开的表格
let unwrap = ({a, c}) => ({a, c});
let unwrap2 = function({a, c}) { return { a, c }; };
let picked = unwrap({ a: 5, b: 6, c: 7 });
let picked2 = unwrap2({a: 5, b: 6, c: 7})
console.log(picked)
console.log(picked2)
如果您使用的是 ES6,有一种非常简洁的方法可以使用解构来做到这一点。解构允许您使用扩展轻松添加对象,但它也允许您以相同的方式创建子集对象。
const object = {
a: 'a',
b: 'b',
c: 'c',
d: 'd',
}
// Remove "c" and "d" fields from original object:
const {c, d, ...partialObject} = object;
const subset = {c, d};
console.log(partialObject) // => { a: 'a', b: 'b'}
console.log(subset) // => { c: 'c', d: 'd'};
两种常见的方法是解构和传统的类似 Lodash 的pick
/omit
实现。它们之间的主要实际区别在于,解构需要一个键列表是静态的,不能省略它们,包括不存在的选择键,即它是包含的。这可能是可取的,也可能是不可取的,并且无法针对解构语法进行更改。
鉴于:
var obj = { 'foo-bar': 1, bar: 2, qux: 3 };
定期选择foo-bar
, bar
,baz
键的预期结果:
{ 'foo-bar': 1, bar: 2 }
包容性采摘的预期结果:
{ 'foo-bar': 1, bar: 2, baz: undefined }
解构语法允许使用函数参数或变量对对象进行解构和重组。
限制是键列表是预定义的,它们不能作为字符串列出,如问题中所述。如果键是非字母数字的,例如foo-bar
.
好处是它是 ES6 自然的高性能解决方案。
缺点是键列表是重复的,如果列表很长,这会导致代码冗长。由于在这种情况下解构复制了对象文字语法,因此可以按原样复制和粘贴列表。
let subset = (({ 'foo-bar': foo, bar, baz }) => ({ 'foo-bar': foo, bar, baz }))(obj);
let { 'foo-bar': foo, bar, baz } = obj;
let subset = { 'foo-bar': foo, bar, baz };
正如问题所要求的那样,任意选择的键列表由字符串组成。这允许不预定义它们并使用包含键名的变量,['foo-bar', someKey, ...moreKeys]
.
ECMAScript 2017 具有Object.entries
和Array.prototype.includes
,ECMAScript 2019 具有Object.fromEntries
,它们可以在需要时进行填充。
考虑到要选择的对象包含额外的键,从列表中迭代键通常比对象键更有效,如果需要省略键,反之亦然。
var subset = ['foo-bar', 'bar', 'baz']
.reduce(function (obj2, key) {
if (key in obj) // line can be removed to make it inclusive
obj2[key] = obj[key];
return obj2;
}, {});
var subset = Object.keys(obj)
.filter(function (key) {
return ['baz', 'qux'].indexOf(key) < 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
let subset = ['foo-bar', 'bar', 'baz']
.filter(key => key in obj) // line can be removed to make it inclusive
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));
let subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));
let subset = Object.fromEntries(
['foo-bar', 'bar', 'baz']
.filter(key => key in obj) // line can be removed to make it inclusive
.map(key => [key, obj[key]])
);
let subset = Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !['baz', 'qux'].includes(key))
);
One-liners 可以表示为类似于 Lodashpick
或 的可重用辅助函数omit
,其中键列表通过参数 , 传递pick(obj, 'foo-bar', 'bar', 'baz')
。
let pick = (obj, ...keys) => Object.fromEntries(
keys
.filter(key => key in obj)
.map(key => [key, obj[key]])
);
let inclusivePick = (obj, ...keys) => Object.fromEntries(
keys.map(key => [key, obj[key]])
);
let omit = (obj, ...keys) => Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !keys.includes(key))
);
虽然它有点冗长,但您可以通过使用Array.prototype.reduce来完成其他人在 2 年前推荐下划线/lodash 的内容。
var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});
这种方法从另一方面解决了它:不是获取一个对象并将属性名称传递给它来提取,而是获取一个属性名称数组并将它们缩减为一个新对象。
虽然在最简单的情况下它更冗长,但这里的回调非常方便,因为您可以轻松满足一些常见要求,例如将新对象上的“颜色”属性更改为“颜色”、展平数组等——任何一个从一个服务/库接收对象并构建其他地方需要的新对象时需要做的事情。虽然 underscore/lodash 是优秀的、实现良好的库,但这是我首选的方法,以减少对供应商的依赖,当我的子集构建逻辑变得更复杂时,这是一种更简单、更一致的方法。
编辑:es7 版本相同:
const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});
编辑:也是咖喱的一个很好的例子!让一个“pick”函数返回另一个函数。
const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});
上面的方法与另一种方法非常接近,只是它可以让您即时构建“选择器”。例如
pick('color', 'height')(elmo);
这种方法特别巧妙的是,您可以轻松地将所选的“选择”传递到任何带有函数的东西中,例如Array#map
:
[elmo, grover, bigBird].map(pick('color', 'height'));
// [
// { color: 'red', height: 'short' },
// { color: 'blue', height: 'medium' },
// { color: 'yellow', height: 'tall' },
// ]