JS - 财产更换

时间:2017-11-22 14:46:15

标签: javascript object properties

亲爱的Stack Overflow社区,
我有一个关于对象属性替换的问题(你可以跳到底部阅读问题)。我正在处理一个应用程序,它从后端获取一个教师对象。后端使用java hibernate来查询数据库并序列化要发送到前端的对象(我)。我得到了这个教师对象,但是对象中有循环引用。通过添加引用ID代替对象来处理哪个java。所以我得到了这个教师对象,并且在对象内部是我需要用实际对象替换的这些引用id,问题在于它们是嵌套对象。我目前所做的是创建一个堆栈,并遍历对象以找到这些id和引用id。当我找到一个id时,我把它放在堆栈上。不是问题,但是当我找到引用ID时,我需要使用我在堆栈中找到的那个来重新引用它。再次这很难做到,因为我通过递归函数来做这些,所以我没有物理对象。

我的问题是,如何替换嵌套对象属性

JS BIN

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);

4 个答案:

答案 0 :(得分:3)

考虑两次迭代数据结构:

  1. 用于填充id-to-object映射的递归遍历(其中键是id,值是实际对象)。您可以在第一次递归调用中初始化映射,然后将其传递给下一个递归级别。

  2. 使用实际对象替换引用的递归遍历。由于您现在有了地图,因此可以就地进行此替换。

  3. Code

答案 1 :(得分:3)

您应首先遍历该对象并收集:

  1. 对象字典 - 对象id到对象引用的映射
  2. 项目清单,其中:
    • 第一个元素是包含要替换的属性的对象
    • 要替换的财产名称
  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)

我不知道你的想法是不是

&#13;
&#13;
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;
&#13;
&#13;

对象noRef在没有找到ref对象的情况下,所以我把它放在当前ref的父对象中,当遍历完成时我循环放到它上面关联未找到参考

答案 3 :(得分:1)

如果直接设置该值。在下一次迭代中,对象将具有循环引用,并最终处于无限循环中。因此,将赋值操作推送到事件队列中(一种简单的方法是调用setTimeout传递0秒)。我希望这会对你有所帮助。

&#13;
&#13;
 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;
&#13;
&#13;