亲爱的Stack Overflow社区,
我有一个关于对象属性替换的问题(你可以跳到底部阅读问题)。我正在处理一个应用程序,它从后端获取一个教师对象。后端使用java hibernate来查询数据库并序列化要发送到前端的对象(我)。我得到了这个教师对象,但是对象中有循环引用。通过添加引用ID代替对象来处理哪个java。所以我得到了这个教师对象,并且在对象内部是我需要用实际对象替换的这些引用id,问题在于它们是嵌套对象。我目前所做的是创建一个堆栈,并遍历对象以找到这些id和引用id。当我找到一个id时,我把它放在堆栈上。不是问题,但是当我找到引用ID时,我需要使用我在堆栈中找到的那个来重新引用它。再次这很难做到,因为我通过递归函数来做这些,所以我没有物理对象。
我的问题是,如何替换嵌套对象属性
var stack = {};
var add = function(prop, data) {
if (prop == '@id') {
stack[data[prop]] = data;
}
if (prop == '@ref') {
// console.log(stack[data[prop]])
// How do i replace the value with the stack object
// test.PROPERTY = stack[data[prop]] is what im looking for
}
}
var traverse = function(object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
add(property, object);
if (object[property] && typeof object[property] === 'object') {
traverse(object[property]);
}
}
}
}
var test = {
'teacher': {
'course': {
"@id": "1",
'students': [{
"@id": "2",
'name': "John",
'course': {
"@ref": "1",
}
}, {
"@id": "2",
'name': "Next",
'course': {
"@ref": "1",
}
}]
}
}
};
setTimeout(() => {
console.log(stack);
// console.log(test);
}, 500);
traverse(test);
答案 0 :(得分:3)
考虑两次迭代数据结构:
用于填充id-to-object映射的递归遍历(其中键是id,值是实际对象)。您可以在第一次递归调用中初始化映射,然后将其传递给下一个递归级别。
使用实际对象替换引用的递归遍历。由于您现在有了地图,因此可以就地进行此替换。
答案 1 :(得分:3)
您应首先遍历该对象并收集:
拥有这样的清单是解决方案的关键。它允许您:避免对test
对象进行第二次递归遍历,并存储足够的信息来替换整个{"@ref":"..."}
对象,而不仅仅是它的内容。
接下来,您应该遍历列表并使用对字典中实际对象的引用替换所有{"@ref":"..."}
个对象。
var test = {
'teacher': {
'course': {
"@id": "1",
'students': [{
"@id": "2",
'name': "John",
'course': {
"@ref": "1",
}
}, {
"@id": "2",
'name': "Next",
'course': {
"@ref": "1",
}
}]
}
}
};
function collectRefs(obj, dic, list) {
obj.hasOwnProperty('@id') && (dic[obj['@id']] = obj); // populate dictionary
Object.keys(obj).forEach(function(key) {
if(obj[key] && typeof obj[key] === 'object') {
if(obj[key].hasOwnProperty('@ref')) {
list.push([obj, key]) // populate list
} else {
collectRefs(obj[key], dic, list) // traverse recursively
}
}
});
return obj;
}
function recover(obj) {
var dic = {}, list = [];
collectRefs(obj, dic, list);
list.forEach(function(item) {
item[0][item[1]] = dic[item[0][item[1]]['@ref']]; // make replacements
});
return obj;
}
console.log(recover(test).teacher.course.students[0].course['@id']); // 1
最后,所有{"@ref": "1"}
都将替换为对象{"@id": "1", ...}
<强> P.S。 强>
顺便说一下,有一个更好的解决方案来使用JSON格式cycle.js的发明者提供的循环引用(Douglas Crockford)序列化/反序列化JSON。它使用JSONPath作为@ref
个对象的值,因此不需要在引用的对象上使用@id
。此外,不需要使用其他数据结构(如上面的示例中的字典和列表)来使用循环引用重建原始对象。因此,如果您可以更改后端序列化对象的方式,我建议您查看它。
答案 2 :(得分:1)
我不知道你的想法是不是
var test = {
'teacher': {
'course': {
"@id": "1",
'students': [{
"@id": "2",
'name': "John",
'course': {
"@ref": "1",
}
}, {
"@id": "2",
'name': "Next",
'course': {
"@ref": "1",
}
}]
}
}
};
let tmpObj = {};
let noRef = {};
function traverse(obj){
Object.keys(obj).forEach(function(v){
if(v !== '@ref' && typeof obj[v] === 'object'){
var newObj = obj[v];
if(obj[v]['@id']){
if(!tmpObj[v]){
tmpObj[v] = {};
}
tmpObj[v][obj[v]['@id']] = obj;
}
if(obj[v]['@ref'] ){
if(tmpObj[v] && tmpObj[v][obj[v]['@ref']]){
obj[v]['@ref'] = tmpObj[v][obj[v]['@ref']][v];
}else{
if(!noRef[v]){
noRef[v] = [];
}
noRef[v].push(newObj)
}
}
traverse(newObj);
}
})
}
traverse(test);
Object.keys(noRef).forEach(function(v){
v.forEach(function(vv){
vv['@ref'] = tmpObj[v][vv['@ref']][v];
})
});
console.log(test);
&#13;
对象noRef
在没有找到ref对象的情况下,所以我把它放在当前ref的父对象中,当遍历完成时我循环放到它上面关联未找到参考
答案 3 :(得分:1)
如果直接设置该值。在下一次迭代中,对象将具有循环引用,并最终处于无限循环中。因此,将赋值操作推送到事件队列中(一种简单的方法是调用setTimeout传递0秒)。我希望这会对你有所帮助。
var stack = {};
var add = function(prop, data) {
if (prop == '@id') {
stack[data[prop]] = data;
}
if (prop == '@ref') {
// console.log(stack[data[prop]])
// How do i replace the value with the stack object
setTimeout(function(){
data[prop] = stack
},0);
}
}
var traverse = function(object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
add(property, object);
if (object[property] && typeof object[property] === 'object') {
traverse(object[property]);
}
}
}
}
var test = {
'teacher': {
'course': {
"@id": "1",
'students': [{
"@id": "2",
'name': "John",
'course': {
"@ref": "1",
}
}, {
"@id": "2",
'name': "Next",
'course': {
"@ref": "1",
}
}]
}
}
};
setTimeout(() => {
console.log(stack);
// console.log(test);
}, 500);
traverse(test);
&#13;