Javascript 如何递归地取消嵌套对象的嵌套?
我正在使用一个数组,该数组可能具有许多不可预测嵌套的对象/数组。每个对象都将始终具有name属性,其中一些对象可能具有sub_字段数组。我希望剥离对象,使每个对象的键等于name属性,同时保持原始对象的嵌套级别 下面是一个我可以开始的例子:Javascript 如何递归地取消嵌套对象的嵌套?,javascript,recursion,Javascript,Recursion,我正在使用一个数组,该数组可能具有许多不可预测嵌套的对象/数组。每个对象都将始终具有name属性,其中一些对象可能具有sub_字段数组。我希望剥离对象,使每个对象的键等于name属性,同时保持原始对象的嵌套级别 下面是一个我可以开始的例子: var data = [ { foo: 'foo', bar: 'bar', name: 'Object 1' }, { foo: 'foo', bar:
var data = [
{
foo: 'foo',
bar: 'bar',
name: 'Object 1'
},
{
foo: 'foo',
bar: 'bar',
name: 'Object 2'
},
{
foo: 'foo',
bar: 'bar',
name: 'Object 3',
sub_fields : [
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 1',
sub_fields: [
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 1',
}
]
},
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 2',
sub_fields: [
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 1',
},
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 2',
},
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 3',
sub_fields: [
{
foo: 'foo',
bar: 'bar',
name: 'SubLevel Object 1'
}
]
}
]
}
]
}
]
理想情况下,我的目标是获取原始数据并创建如下内容:
var newData = {
'Object 1': null,
'Object 2': null,
'Object 3': {
'Sublevel Object 1': {
'Sublevel Object 1': null
},
'Sublevel Object 2': {
'Sublevel Object 1': null,
'Sublevel Object 2': null,
'Sublevel Object 3': {
'Sublevel Object 1': null
}
}
}
}
我觉得我花了太多的时间在这上面,而我知道一定有某种递归函数来实现它——我只是对我正在努力寻找解决方案的这类事情太缺乏经验了
有人能告诉我我能做什么吗
编辑
这是我试过的。这是一个混乱的局面,事实上太零碎了,太尴尬了哈哈,但是,真诚地说,我不想让你认为我没有试着自己解决这个问题:
另外,正在解析的字段组来自我正在处理的更相关的JSON数据。。。不是上面的foo/bar示例
var parsed_field_group = JSON.parse(JSON.stringify(field_group));
var fields = field_group[0].fields;
function removeKeys(obj, keys) {
for (var prop in obj) {
if(obj.hasOwnProperty(prop)) {
switch(typeof(obj[prop])) {
case 'object':
if(keys.indexOf(prop) > -1) {
delete obj[prop];
} else {
removeKeys(obj[prop], keys);
}
break;
default:
if(!keys.includes(prop)) {
delete obj[prop];
}
break;
}
}
}
}
removeKeys(fields, ['name', 'wrapper'])
console.log(fields)
var newJson = Object.create(null);
fields.forEach(field => {
if (field.hasOwnProperty('sub_fields')) {
newJson[field.name] = field
} else {
newJson[field.name] = null
}
})
function handleSubFields(obj, key) {
for (var prop in obj) {
if (obj[prop]) {
obj[prop][key].forEach(field => {
if (field.hasOwnProperty(key)) {
obj[prop][field.name] = field
} else {
obj[prop][field.name] = null
}
})
delete obj[prop][key]
delete obj[prop].name
}
}
}
handleSubFields(newJson, 'sub_fields')
console.log(newJson)
上面的内容让我非常接近,但我最终被困在嵌套在对象第一个子字段中的子字段上
我知道上面的话一定很可笑。请善待我!:- 是的,使用递归非常容易。以下是您的基本情况: 您收到的不是对象的东西-返回null。 那么您的基本递归情况是: 收到一个对象后,用一个属性构造一个新对象,该属性的名称是对象的名称,值是函数的递归应用程序。 最后,如果您有数组,您只需处理发生的情况: 遍历整个数组并针对每个元素调用函数。将所有结果收集到同一对象中。 var data=[{foo:'foo',bar:'bar',name:'Object 1'},{foo:'foo',bar:'bar',name:'Object 2'},{foo:'foo',bar:'bar',name:'Object 3',sub_字段:[{foo:'foo',bar:'bar',name:'sub sub sub_字段:[{foo:'foo:'foo',bar:'bar',name:'subvel Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',sub_字段:[{foo:'foo',bar:'subvell Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',},{foo:'foo',bar:'bar',name:'subvell Object 3',sub sub_字段:[{foo:'foo:'foo',bar:'bar:'bar',name:'subvell Object 1'}]}] 函数toObjdata{ 如果数据==null,则返回null; //在同一对象中收集对函数的所有递归调用 if Array.isArraydata return data.reduceac,data=>{…acc,…toObjdata},{} //从'name'属性和函数的递归应用程序生成新对象 返回{[data.name]:toObjdata.sub_字段,{}; }
console.logtoObjdata是的,使用递归非常容易。下面是您的基本情况: 您收到的不是对象的东西-返回null。 那么您的基本递归情况是: 收到一个对象后,用一个属性构造一个新对象,该属性的名称是对象的名称,值是函数的递归应用程序。 最后,如果您有数组,您只需处理发生的情况: 遍历整个数组并针对每个元素调用函数。将所有结果收集到同一个对象中。 var data=[{foo:'foo',bar:'bar',name:'Object 1'},{foo:'foo',bar:'bar',name:'Object 2'},{foo:'foo',bar:'bar',name:'Object 3',sub_字段:[{foo:'foo',bar:'bar',name:'sub sub sub_字段:[{foo:'foo:'foo',bar:'bar',name:'subvel Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',sub_字段:[{foo:'foo',bar:'subvell Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',},{foo:'foo',bar:'bar',name:'subvell Object 3',sub sub_字段:[{foo:'foo:'foo',bar:'bar:'bar',name:'subvell Object 1'}]}] 函数toObjdata{ 如果数据==null,则返回null; //在同一对象中收集对函数的所有递归调用 if Array.isArraydata return data.reduceac,data=>{…acc,…toObjdata},{} //从'name'属性和函数的递归应用程序生成新对象 返回{[data.name]:toObjdata.sub_字段,{}; } console.logtoObjdata转换
{ foo: 'foo'
, bar: 'bar'
, name: 'Object 1'
}
进入:
您可以使用此功能:
const transform =
({name}) =>
({[name]: null});
如果它包含子字段呢
将变换的结果指定给sub_字段中的所有对象,而不是指定null
我们可以修改transform以支持:
const transform =
({name, sub_fields}) =>
( { [name]: sub_fields
? Object.assign({}, ...sub_fields.map(transform))
: null
}
);
现在,您可以将相同的原理应用于我们的阵列以生成最终对象:
常数变换=
{name,sub_fields}=>
{[name]:子字段
?Object.assign{},…sub_fields.maptransform
:null
}
;
console.log
Object.assign{},…data.maptransform
;
const data=[{foo:foo,bar:bar,name:Object 1},{foo:foo,bar:bar,name:Object 2},{foo:f
oo,bar:bar,name:Object 3,sub_字段:[{foo:foo,bar:bar,name:SubLevel Object 1,sub_字段:[{foo:foo,bar:bar,name:SubLevel Object 1}},{foo:foo,bar:bar,name:SubLevel Object 2,sub_字段:[{foo:foo,bar:bar,name:SubLevel Object 1},{foo:foo,bar:bar,name:SubLevel Object 2},{foo:foo,bar:bar,name:SubLevel Object 3,SubLevel字段:[{foo:foo,bar:bar,名称:子级对象1}]}]}]}];
转化
{ foo: 'foo'
, bar: 'bar'
, name: 'Object 1'
}
进入:
您可以使用此功能:
const transform =
({name}) =>
({[name]: null});
如果它包含子字段呢
将变换的结果指定给sub_字段中的所有对象,而不是指定null
我们可以修改transform以支持:
const transform =
({name, sub_fields}) =>
( { [name]: sub_fields
? Object.assign({}, ...sub_fields.map(transform))
: null
}
);
现在,您可以将相同的原理应用于我们的阵列以生成最终对象:
常数变换=
{name,sub_fields}=>
{[name]:子字段
?Object.assign{},…sub_fields.maptransform
:null
}
;
console.log
Object.assign{},…data.maptransform
;
const data=[{foo:foo,bar:bar,name:Object 1},{foo:foo,bar:bar,name:Object 2},{foo:foo,bar:bar,name:Object 3,sub_字段:[{foo:foo,bar:bar,name:SubLevel Object 1,sub_字段:[{foo:foo,bar:bar,name:SubLevel Object 1},{foo:foo,bar:bar,name:SubLevel Object 2,SubLevel fields:[{foo:foo,bar,bar:SubLevel,{foo:foo,bar:bar,名称:子级对象3,子_字段:[{foo:foo,bar:bar,名称:子级对象1}]}];
我使用递归开发这个函数来实现您的目标,因为我知道在您的对象中总是有一个名为“name”的属性,有时还有其他名为“sub_fields”的属性
function rebuildNameObject(objArr = [], resObj = {}){
objArr.forEach(obj => {
Object.keys(obj).forEach(item => {
if (item === 'name'){
resObj[obj[item]] = null;
}
if(item === 'sub_fields'){
resObj[obj['name']] = rebuildNameObject(obj[item]);
}
});
});
return resObj;
}
我使用递归开发这个函数来实现您的目标,因为我知道在您的对象中总是有一个名为“name”的属性,有时还有其他名为“sub_fields”的属性
function rebuildNameObject(objArr = [], resObj = {}){
objArr.forEach(obj => {
Object.keys(obj).forEach(item => {
if (item === 'name'){
resObj[obj[item]] = null;
}
if(item === 'sub_fields'){
resObj[obj['name']] = rebuildNameObject(obj[item]);
}
});
});
return resObj;
}
这里已经有好几个很好的答案,还有customcommander的一个很好的解释。我认为这种选择值得考虑,原因有几个: 只有在没有更明确的内容时,我才选择reduce。虽然它是最强大的数组迭代方法,但它的级别较低,对我来说,它不像map和Object.assign的组合那样具有自文档功能。与forEach相比,这更为重要,forEach根本没有固有的语义 我个人更喜欢使用表达式而不是语句,并且更喜欢使用单个表达式箭头函数,而不是使用一个或多个返回语句 虽然customcommander的阐述很棒,而且我非常喜欢这种技术,但是转换函数仍然需要一个包装器来获得预期的输出。包装器绝对没有问题,尽管我会为此添加另一个函数。但是我在这里提出的版本使用类似的技术管理同样的事情,并且不需要包装就不会增加复杂性 常量转换=obj=> 数组。isArray对象 ? Object.assign…obj.map变换 :{[obj.name]:'sub_fields'在obj中?transform obj.sub_fields:null} const data=[{foo:'foo',bar:'bar',name:'Object 1'},{foo:'foo',bar:'bar',name:'Object 2'},{foo:'foo',bar:'bar',name:'Object 3',sub_字段:[{foo:'foo',bar:'bar',name:'subvel Object 1',sub sub_字段:[{foo:'foo:'foo',bar:'bar',name:'subvel Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',sub_字段:[{foo:'foo',bar:'subvell Object 1',},{foo:'foo',bar:'bar',name:'subvell Object 2',},{foo:'foo',bar:'bar',name:'subvell Object 3',sub sub_字段:[{foo:'foo:'foo',bar:'bar:'bar',name:'subvell Object 1'}]}]
console.log transform data已经有了几个很好的答案,customcommander也给出了一个很好的解释。我认为这个替代方案值得考虑,原因有几个: 只有在没有更明确的方法可用时,我才选择reduce。虽然它是最强大的数组迭代方法,但它的级别较低,而且对我来说,它不像map和Object.assign的组合那样具有自文档功能。与forEach相比,这一点更为重要,因为forEach根本没有固有的语义 我个人更喜欢使用表达式而不是语句,并且更喜欢使用单个表达式箭头函数,而不是使用一个或多个返回语句 虽然customcommander的阐述很棒,而且我非常喜欢这项技术,但转换函数仍然需要一个包装器来获得预期的输出。包装器绝对没有问题,尽管我会为此添加另一个函数。但是我在这里提出的版本使用类似的技术管理同样的事情,并且没有添加复杂的内容不需要包装就可以实现 常量转换=obj=> 数组。isArray对象 ?对象分配…对象映射变换 :{[obj.name]:'sub_fields'在obj中?transform obj.sub_fields:null} const data=[{foo:'foo',bar:'bar',name:'Object 1'},{foo:'foo',bar:'bar',name:'Object 2'},{foo:'foo',bar:'bar',name:'Object 3',子字段 :[{foo:'foo',bar:'bar',name:'SubLevel Object 1',sub_字段:[{foo:'foo',bar:'bar',name:'SubLevel Object 1',name:'SubLevel Object 1',sub_字段:[{foo:'foo',bar:'bar',name:'SubLevel Object 1',},{foo:'foo',bar:'bar',name:'SubLevel Object 2',},{foo:'foo',bar:'bar',name:'SubLevel Object 3',sub_字段:[{foo:'foo',bar:'bar',name:'SubLevel Object 1'}]}]
log转换数据如果它不是一个字符串,它不是。你能告诉我们你尝试了什么吗?它不需要工作或完成,我们只是希望看到一个诚信的努力第一。Andreas是正确的,问题中的不是JSON,而是一个对象文字表达式。我编写了一个解决方案,并注意到,对于Sublevel object 3:null,您的预期结果是奇怪的,因为输入中有一个sub_字段。@Andreas如果它不是字符串,它不是JSON,您可能需要澄清该语句。布尔和数字是有效的JSON实体example@customcommander是的,说JSON是字符串并不完全准确;更准确的是,JSON是包含定义良好的结构的文本。我们之所以说它是字符串,是因为在任何编程语言中使用它时,它都是字符串。对象文字表达式通常被错误地称为JSON,但因为它们不是字符串,所以实际上不是JSON。在这个问题中,对象键不是双引号,这对于JSON是必需的,但对于对象文本表达式则不是。JSON实际上是JS对象的字符串表示。如果它不是字符串,那么它就不是。您能告诉我们您尝试了什么吗?它不需要工作或完成,我们只是希望看到一个诚信的努力第一。Andreas是正确的,问题中的不是JSON,而是一个对象文字表达式。我编写了一个解决方案,并注意到,对于Sublevel object 3:null,您的预期结果是奇怪的,因为输入中有一个sub_字段。@Andreas如果它不是字符串,它不是JSON,您可能需要澄清该语句。布尔和数字是有效的JSON实体example@customcommander是的,说JSON是字符串并不完全准确;更准确的是,JSON是包含定义良好的结构的文本。我们之所以说它是字符串,是因为在任何编程语言中使用它时,它都是字符串。对象文字表达式通常被错误地称为JSON,但因为它们不是字符串,所以实际上不是JSON。在这个问题中,对象键不是双引号,这对于JSON是必需的,但对于对象文本表达式则不是。JSON实际上是JS对象的字符串表示。非常感谢。这比我尝试的要优雅和简洁得多。证明我还有很多东西要学。我真的很感谢你抽出时间来帮忙。非常感谢。这比我尝试的要优雅和简洁得多。证明我还有很多东西要学。我真的很感谢你花时间来帮助我。有没有任何强有力的理由来解释{[name]:!sub_fields?null:Object.assign{},…sub_fields.maptransform}超过{[name]:sub_fields?Object.assign{},…sub_fields.maptransform:null}。我找到了!更难注意到。{[name]:!sub_fields?null:Object.assign{},…sub_fields.maptransform}覆盖{[name]:sub_fields?Object.assign{},…sub_fields.maptransform:null}的原因有哪些。我找到了!更难注意。如果不先合并到一个空对象中,是否会对…obj.maptransform中的第一个元素进行变异?会,但无论如何,这是一个一次性对象。如果不先合并到一个空对象中,是否会对…obj.maptransform中的第一个元素进行变异?会,但无论如何,这是一个一次性对象。