假设我有一个对象:
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' }
我怎样才能做到呢?
当前回答
虽然它有点啰嗦,但通过使用Array.prototype.reduce,你可以完成其他人在2年前推荐的underscore/lodash。
var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});
这种方法从另一方面解决了这个问题:而不是获取一个对象并将属性名传递给它来提取,而是获取一个属性名数组并将它们简化为一个新对象。
While it's more verbose in the simplest case, a callback here is pretty handy, since you can easily meet some common requirements, e.g. change the 'color' property to 'colour' on the new object, flatten arrays, etc. -- any of the things you need to do when receiving an object from one service/library and building a new object needed somewhere else. While underscore/lodash are excellent, well-implemented libs, this is my preferred approach for less vendor-reliance, and a simpler, more consistent approach when my subset-building logic gets more complex.
编辑: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] }), {});
上面的方法非常接近于另一种方法,除了它让你在运行中构建一个“picker”。如。
pick('color', 'height')(elmo);
这种方法的特别之处在于,你可以很容易地将所选的“picks”传递到任何带有函数的对象中,例如Array#map:
[elmo, grover, bigBird].map(pick('color', 'height'));
// [
// { color: 'red', height: 'short' },
// { color: 'blue', height: 'medium' },
// { color: 'yellow', height: 'tall' },
// ]
其他回答
两种Array.prototype.reduce:
const selectable = {a: null, b: null};
const v = {a: true, b: 'yes', c: 4};
const r = Object.keys(selectable).reduce((a, b) => {
return (a[b] = v[b]), a;
}, {});
console.log(r);
这个答案使用了神奇的逗号运算符: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
如果你想要更花哨,这个更紧凑:
const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});
把所有这些放到一个可重用的函数中:
const getSelectable = function (selectable, original) {
return Object.keys(selectable).reduce((a, b) => (a[b] = original[b], a), {})
};
const r = getSelectable(selectable, v);
console.log(r);
对象数组
const aListOfObjects = [{
prop1: 50,
prop2: "Nothing",
prop3: "hello",
prop4: "What's up",
},
{
prop1: 88,
prop2: "Whatever",
prop3: "world",
prop4: "You get it",
},
]
创建一个或多个对象的子集可以通过这种方式解构对象来实现。
const sections = aListOfObjects.map(({prop1, prop2}) => ({prop1, prop2}));
扩展内置原型有其优势。如果你使用Object.defineProperty,你不会产生任何污染-唯一剩下的问题是与未来属性的冲突(例如,你定义Object.prototype。在将来,ES标准将Object.prototype.slice定义为具有不同的功能-现在您的代码正在破坏Object.prototype.slice中应该存在的预期功能)。
...... ....... ...... .....Elmo的身高不是未知的(!!)
Object.defineProperty(Object.prototype, 'slice', { 可列举的:假的, 可写:没错, 值:function(…args) { 让o = {}; for (let k of args) this. hasownproperty (k) && (o[k] = this[k]); 返回啊; } }); Elmo = { 颜色:红色, 讨厌:没错, 高度:“24”, Meta: {1: '1', 2: '2'} }; console.log(艾尔摩。片(“颜色”、“高度”)); console.log('看,没有污染:); 对于(let k in elmo) console.log(' - ${k} ');
我建议看看Lodash;它有很多实用函数。
例如,pick()就是你要找的东西:
var subset = _.pick(elmo, ['color', 'height']);
小提琴
两种常见的方法是解构和传统的类似lodash的选择/省略实现。它们之间的主要实际区别是,解构需要一个键列表是静态的,不能省略它们,包括不存在的选择键,即它是包含的。这可能是可取的,也可能是不可取的,并且不能为析构语法而更改。
考虑到:
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来说是很自然的。
缺点是键的列表是重复的,在列表很长的情况下,这会导致冗长的代码。由于在这种情况下解构会重复对象文字语法,因此可以原样复制和粘贴列表。
IIFE
const subset = (({ 'foo-bar': foo, bar, baz }) => ({ 'foo-bar': foo, bar, baz }))(obj);
临时变量
const { 'foo-bar': foo, bar, baz } = obj;
const subset = { 'foo-bar': foo, bar, baz };
字符串列表
任意选择的键列表由字符串组成,如问题所要求的。这允许不预定义它们,并使用包含键名的变量,['foo-bar', someKey,…moreKeys]。
ECMAScript 2017有Object。entry和Array.prototype。包括,ECMAScript 2019有object . fromentry,它们可以在需要时填充。
一行程序
考虑到要选择的对象包含额外的键,通常从列表中迭代键比从对象键更有效,如果需要省略键,反之亦然。
Pick (ES5)
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;
}, {});
自己的 (ES5)
var subset = Object.keys(obj)
.filter(function (key) {
return ['baz', 'qux'].indexOf(key) < 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
拾取 (ES6)
const 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), {});
省略 (ES6)
const subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});
挑选 (ES2019)
const 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]])
);
省略 (ES2019)
const subset = Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !['baz', 'qux'].includes(key))
);
可重用的功能
一行程序可以表示为类似于Lodash pick或省略的可重用帮助函数,其中通过参数pick(obj, 'foo-bar', 'bar', 'baz')传递键列表。
const pick = (obj, ...keys) => Object.fromEntries(
keys
.filter(key => key in obj)
.map(key => [key, obj[key]])
);
const inclusivePick = (obj, ...keys) => Object.fromEntries(
keys.map(key => [key, obj[key]])
);
const omit = (obj, ...keys) => Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !keys.includes(key))
);