我有一个对象数组,我需要以某种方式解析。对象具有一组公共字段和一些不在所有对象中的可选字段。为了示例,a,b,c
是公共字段,d,e,f
是可选字段。我想对某些字段执行某些操作,例如将a
和d
的值加倍,同时保留其余字段。如果一个对象缺少一个或多个可选字段,它应该保留在结果中。
问题在于我想以纯粹的功能方式完成它,而不是声明一个空数组并推入它。
示例输入:
const input = [
{
a: 3,
b: 'test',
c: 34,
d: 'example'
},
{
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
预期结果:
[
{
a: 6,
b: 'test',
c: 34,
d: 'EXAMPLE'
},
{
a: 12,
b: 'another',
c: 0,
e: true,
f: () => {}
}
]
到目前为止,我尝试使用map
,如此:
const result = input.map(x => ({
a: 2 * x.a,
d: x.d.toUppercase()
});
或者像这样:
const result = input.map(x => ({
a: 2 * x.a,
b,
c,
d: x.d.toUppercase(),
e,
f
});
但是这导致对象只包含被操纵的字段,或者包含所有字段,无论它们是否存在于原始对象中。
有关如何实现这一目标的任何建议吗?
答案 0 :(得分:2)
如果希望对象是不可变的,则需要在进行所需更改之前复制对象。 Object.assign
允许您在一个表达式中执行此操作:
const result = input.map(x => Object.assign({}, x,
{ a: 2 * x.a },
('d' in x) && { d: x.d.toUpperCase() }
));
const input = [{
a: 3,
b: 'test',
c: 34,
d: 'example'
}, {
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
const result = input.map(x => Object.assign({},
x,
{ a: 2 * x.a },
('d' in x) && { d: x.d.toUpperCase() }
));
console.log(result);
.as-console-wrapper { min-height: 100%; }
答案 1 :(得分:1)
此解决方案需要Babel's Object rest spread transform插件,因为Object Rest/Spread Properties for ECMAScript仍处于提案阶段。
创建一个新对象。将原始对象传播到新对象中。如果要更改的属性存在于原始对象中,请将具有新属性值的对象返回到扩展。如果没有,请返回null
(点差忽略undefined
和null
)。新值将覆盖旧值。
const input = [{
a: 3,
b: 'test',
c: 34,
d: 'example'
},
{
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
const result = input.map(x => ({
...x, // spread all props of x
...('a' in x ? { a: 2 * x.a } : null), // override a if exists
...('d' in x ? { d: x.d.toUpperCase() } : null) // override d if exists
}));
console.log(result);
答案 2 :(得分:0)
如果要更改其值的密钥存在,则可以使用map
方法并检查每个对象。如果是这样,那么yuo可以相应地改变密钥的值。
const input = [
{
a: 3,
b: 'test',
c: 34,
d: 'example'
},
{
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
const result = input.map(function(obj){
if(obj.a){
obj.a = 2 * obj.a;
}
if(obj.d){
obj.d = obj.d.toUpperCase()
}
return obj;
});
console.log(result);
答案 3 :(得分:0)
map
函数。在这种情况下,您可以检查对象中是否存在键a
,然后仅对其值执行必要的操作。
希望此代码段有用
const input = [{
a: 3,
b: 'test',
c: 34,
d: 'example'
},
{
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
input.map(function(elem, index) {
if (elem.hasOwnProperty('a')) {
elem.a = elem['a'] * 2;
}
})
console.log(input)
答案 4 :(得分:0)
在map函数中,您可以先添加条件来操作输入,然后需要返回修改后的对象。
const input = [
{
a: 3,
b: 'test',
c: 34,
d: 'example'
},
{
a: 6,
b: 'another',
c: 0,
e: true,
f: () => {}
}
];
let output = input.map(o => {
if(o.a) { o.a = o.a * 2; }
if(o.d) { o.d = o.d.toUpperCase(); }
return o;
});
console.log(output)
输出:
[
{
"a": 6,
"b": "test",
"c": 34,
"d": "EXAMPLE"
},
{
"a": 12,
"b": "another",
"c": 0,
"e": true,
"f": () => {}
}
]
答案 5 :(得分:0)
可能值得用Google搜索镜头。许多"功能性" javascript库实现它们以轻松链接您描述的各种数据修改。
在幕后,他们可能会使用您已经找到的相同Object.assign
方法,但它们允许可读,可重复使用且易于链接的代码。
快速&脏实现以显示语法的变化(es6):
const propLens = prop => ({
get: obj => obj[prop],
// Return a new object with the property set to
// a value
set: (val, obj) => Object.assign({}, obj, {
[prop]: val
}),
// Return a new object with the property mapped
// by a function
over: (fn, obj) => Object.assign({}, obj, {
[prop]: fn(obj[prop])
})
});
// Helpers to create specific set/over functions
const set = (lens, val) => obj => lens.set(val, obj);
const over = (lens, fn) => obj => lens.over(fn, obj);
// Some utils for the example
const { pipe, double, toUpper } = utils();
// Now, you can define your modification by a set
// of lens + set/over combinations
const convertData = pipe(
set( propLens("d"), "added" ),
over( propLens("a"), double ),
over( propLens("b"), toUpper )
);
// Example data
const item = {a: 5, b: "hello", c: "untouched"};
// Converted copy
const convertedItem = convertData(item);
console.log("Converted:", convertedItem);
console.log("item !== convertedItem ->", item !== convertedItem);
// Utils
function utils() {
return {
pipe: (...fns) => x =>
fns.reduce((acc, f) => f(acc), x),
double: x => x * 2,
toUpper: x => x.toUpperCase()
};
}

(请注意,此示例的运行速度比在一个函数中执行所有操作要慢一些,但您可能只会注意到大量数据)
如果您喜欢镜片方法,我建议使用维护良好且经过测试的库(如Ramda)。您甚至可以获得更多令人敬畏的功能,如索引镜头和镜头路径,并且默认情况下都会进行调整。