Pregunta JS - reemplazo de propiedad


Estimada comunidad de Stack Overflow,
Tengo una pregunta sobre el reemplazo de propiedad del objeto (puede saltar al final para leer la pregunta) Estoy trabajando en una aplicación que obtiene un objeto maestro del Back-end. El Back-end está utilizando java hibernate para una consulta de la base de datos y serializar el objeto que se enviará a la interfaz (me). Obtengo este objeto maestro, pero hay referencias circulares en el objeto. Que java maneja al agregar una identificación de referencia en lugar del objeto. Así que obtengo este objeto maestro, y dentro del objeto están estos identificadores de referencia que necesito reemplazar con el objeto real, el problema es que son objetos anidados. Lo que hago actualmente es crear una pila y recorrer los objetos para encontrar estos identificadores e identificadores de referencia. Cuando encuentro una identificación, la pongo en la pila. No es un problema, pero cuando encuentro una identificación de referencia, necesito pagarla con la que encontré en la pila. Una vez más, esto es difícil de hacer porque hago esto a través de una función recursiva, por lo que no tengo el objeto físico.

Mi pregunta es, ¿cómo puedo reemplazar las propiedades de objetos anidados? 

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

9
2017-11-22 14:46


origen


Respuestas:


Considere iterar sobre la estructura de datos dos veces:

  1. Recorrido recursivo para rellenar un mapa de id a objeto (donde las claves son identificadores y los valores son los objetos reales). Puede inicializar el mapa en la primera llamada recursiva y luego pasarlo a los siguientes niveles de recursión.

  2. Recorrido recursivo para reemplazar las referencias con los objetos reales. Como ahora tiene un mapa, este reemplazo se puede realizar en el lugar.

Código


3
2017-11-25 19:01



Primero debe atravesar el objeto y recopilar:

  1. diccionario de objetos - mapa de la identificación del objeto a la referencia del objeto
  2. lista de artículos, donde:
    • primer elemento es objeto que contiene propiedad para reemplazar
    • nombre de la propiedad para reemplazar

Tener una lista así es la clave de la solución. Les permite a ambos: evitar la segunda poligonal recursiva de test objeto, y almacenar suficiente información para reemplazar el conjunto {"@ref":"..."} objeto y no solo su contenido.

A continuación, debe recorrer la lista y reemplazar todo {"@ref":"..."} objetos con referencias a objetos reales del diccionario.

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

Al final todo {"@ref": "1"} será reemplazado con referencia al objeto {"@id": "1", ...}

PD

Por cierto, hay una mejor solución para serializar / deserializar JSON con referencias circulares (cycle.js) proporcionado por el inventor del formato JSON Douglas Crockford. Utiliza JSONPath como valor de @ref objetos y, por lo tanto, no requiere tener @id en objetos referidos. Además, no es necesario tener estructuras de datos adicionales (como el diccionario y la lista del ejemplo anterior) para reconstruir el objeto original con referencias circulares. Entonces, si puede cambiar la manera en que su Back-end serializa los objetos, le recomiendo que los revise.


3
2017-11-25 22:56



No sé si es lo que quieres

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

El objeto noRefes en el caso de que el objeto de referencia no se haya encontrado todavía, así que puse en él el objeto padre de la referencia actual, y cuando la poligonal ha terminado, le doy un bucle para poner la asociación en la referencia no encontrada


1
2017-12-02 09:51



Si establece directamente el valor. en las siguientes iteraciones, el objeto tendrá una referencia circular y terminará en un bucle infinito. Entonces, inserte la operación de asignación en la cola de eventos (una forma simple de hacerlo es llamar a setTimeout pasando 0 segundos). Espero que esto ayude.

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


1
2017-12-02 16:42