Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: Stringify a shallow copy of a circular referenced object?

Is there a way to get just a shallow copy of the below to only get one layer deep? I have a means to fix this using a completely different design but I was wondering if anyone else had ran into what I am trying to convert to a string before.

var SomeObjClass = function() {
    var self = this;
    this.group = {
        members: [self]
    };
};
var p = new SomeObjClass();
var str = JSON.stringify(p);
like image 583
simon Avatar asked Oct 17 '22 23:10

simon


2 Answers

It's a little unclear what you're asking, but if your goal is to simply stringify a circular object, you'll have to override toJSON to specify how you'd like your object to be represented

function SomeObjClass () {
  var self = this;
  this.group = {
    members: [self]
  };
}

SomeObjClass.prototype.addMember = function(m) {
  this.group.members.push(m);
};

// when stringifying this object, don't include `self` in group.members
SomeObjClass.prototype.toJSON = function() {
  var self = this;
  return {
    group: {
      members: self.group.members.filter(function (x) {
        return x !== self
      })
    }
  };
}

var a = new SomeObjClass();
var b = new SomeObjClass();
a.addMember(b);

console.log(JSON.stringify(a))

This is probably the best I can help you without seeing more of your code. I don't know how you're using this code, but whatever the case, this is probably not the best design for your class. If you share the rest of the class and the code that uses it, we can probably help you more effectively.

like image 184
Mulan Avatar answered Oct 20 '22 21:10

Mulan


If you check MDN reference about JSON.stringify, you'll see that it accepts as a second parameter a replacer function. This function is useful to massage a bit the elements that you want stringified.

It could help you to avoid your circular problem.

For instance:

function avoidCircularReference(obj) {
  return function(key, value) {
    return key && typeof value === 'object' && obj === value ? undefined : value;
  };
}

var SomeObjClass = function() {
    var self = this;
    this.group = {
        members: [self, {a:'f', b: [self]}]
    };
};
var p = new SomeObjClass();
var str = JSON.stringify(p, avoidCircularReference(p));
console.log(str);

However, and as stated in the documentation and shown running the example:

Note: You cannot use the replacer function to remove values from an array. If you return undefined or a function then null is used instead.

So you'd have to deal in some way with these nulls. Anyway, you could play with this function and "adapt" it to your needs. For instance and applied to your example:

function avoidCircularReference(obj) {
  var removeMeFromArray = function(arr) {
    var index = arr.indexOf(obj);
    if (index > -1) {
      arr.splice(index, 1);
    }
  };

  return function(key, value) {
    if (Object.prototype.toString.call(value) === "[object Array]") {
      removeMeFromArray(value);
    }
    return value;
  };
}

var SomeObjClass = function() {
  var self = this;
  this.group = {
    members: [self, {
      a: 'f',
      b: [self]
    }]
  };
};
var p = new SomeObjClass();
var str = JSON.stringify(p, avoidCircularReference(p));
console.log(str);
like image 39
acontell Avatar answered Oct 20 '22 23:10

acontell