【问题标题】:JS - property replacementJS - 属性替换
【发布时间】:2018-05-06 08:36:20
【问题描述】:

尊敬的 Stack Overflow 社区,
我有一个关于对象属性替换的问题(您可以跳到底部阅读问题)。我正在开发一个从后端获取教师对象的应用程序。后端使用 java hibernate 来查询数据库并序列化要发送到前端(我)的对象。我得到了这个教师对象,但对象中有循环引用。哪个 java 通过添加引用 id 代替对象来处理。所以我得到了这个教师对象,对象内部是我需要用实际对象替换的这些参考 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);

【问题讨论】:

  • 除了噪音,你还有什么,你想要什么?请在更改前后添加数据。
  • @NinaScholz 我目前没有对数据做任何事情,因为我不确定如何从我的函数中引用对象数据。有意义吗?
  • @ref 是有原因的。如果您尝试用实际对象替换 ids,您可能会遇到循环引用,这会导致内存泄漏(这将导致最大调用堆栈大小错误)。
  • 你想要的最终对象模式是什么?
  • 尽量保持你的前端轻量,保持你的编码灵活,并要求你的服务器完成工作。如果您无法更改服务器端,请使用@Vadim 的解决方案

标签: javascript object properties


【解决方案1】:

考虑对数据结构进行两次迭代:

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

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

Code

【讨论】:

  • 我想到了这一点,但我仍然遇到问题,我不知道什么是什么的属性。如果这可以解决问题,请发布一个示例,因为我不知道如何做到这一点。
  • 只是为了让我理解 - 学生“John”和“Next”的“课程”属性应该包含 ID 为“1”的课程,而不是对象“{ref:”1”} “?
  • 你是对的。我需要将其替换为@id:1 的课程对象。阻碍我的是对象的属性。我看不到如何找出正确对象的路径,至少使用我的代码。你建议的一个例子会很棒,这件事让我很难过。
  • 这会导致循环替换。
【解决方案2】:

你应该先遍历对象并收集:

  1. 对象字典 - 对象 ID 到对象引用的映射
  2. 项目列表,其中:
    • 第一个元素是包含要替换的属性的对象
    • 要替换的属性名称

拥有这样的清单是解决问题的关键。它允许您:避免第二次递归遍历 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", ...}

附言

顺便说一句,JSON 格式的发明者Douglas Crockford 提供了一个更好的解决方案来使用循环引用 (cycle.js) 对 JSON 进行序列化/反序列化。它使用JSONPath 作为@ref 对象的值,因此不需要在引用对象上使用@id。此外,不需要额外的数据结构(如上面示例中的字典和列表)来重建具有循环引用的原始对象。因此,如果您可以更改后端序列化对象的方式,我建议您研究一下。

【讨论】:

  • 看起来不错!但是这里的最后一个方括号item[0][item[1]] = dic[item[0][item[1]]['@ref']]]; 是多余的,这就是代码 sn -p 不起作用的原因
【解决方案3】:

不知道是不是你想要的

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

对象noRef是在尚未找到ref对象的情况下,所以我将当前ref的父对象放入其中,当遍历完成时,我循环它以将关联放入not found ref

【讨论】:

    【解决方案4】:

    如果直接设置值。在接下来的迭代中,对象将具有循环引用,并且您最终会陷入无限循环。所以将赋值操作推入事件队列(一个简单的方法是调用 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);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-11
      • 2022-01-24
      • 2013-03-06
      • 2012-11-15
      • 2010-10-02
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      相关资源
      最近更新 更多