2

I have a big object (it has around 200 properties) and I want to print it like so:

[property1: alice, property2: bob, property3: 42, ...]

If the property is a function, I want it to print the code of the function and if it is an array, I want it to print every element of that array. Also, if the property is an object it should also print it's properties and so on...

I have tried to implement this recursively but of course the call stack got too big quite quickly. Then I moved on to a iterative implementation using a stack. Here is what I've got:

function getPropertyString(obj) {
    var res = "";
    var stack = [];
    stack.push(obj);
    while(stack.length > 0){
        var object = stack.pop();
        res += "[";
        for(var prop in object) {
            if(prop == null) continue;
            if(typeof object[prop] === 'object') {
                stack.push(object[prop]);
            } else {
                res += prop + ": " + 
                       object[prop].toString().replace(/[\t\r\n]/g, "") + ", ";
            }
        }
        res += "],";
    }
    return res;
}

This works fine if you have an object like

var a = {
    b : {
        c : "hello",
        d : "world"
    },
    e : "alice",
    f : "bob",
    g : function() {
        console.log("hello");
    },
    h : [1, 2, 3]
}

but let's say you modify a so that a.x = {}; a.x.prototype = a;. Then my function would get stuck in an infinite loop.

How could I get around this?

2
  • This has nothing to do with the prototype chain (or .prototype properties), circular references can always happen. (simplest example: var a = {}; a.x = a;) Commented Oct 18, 2016 at 15:40
  • 1
    Thanks for pointing that out. Should I change the title then? Commented Oct 18, 2016 at 15:42

2 Answers 2

2

Create an array of objects that has been processed from the stack, and do not process them again: (I marked the lines I added to do this)

function getPropertyString(obj) {
    var res = "";
    var stack = [];
    var objectHistory = []; // added this
    stack.push(obj);
    while(stack.length > 0){
        var object = stack.pop();
        if (objectHistory.indexOf(object) != -1) continue;  // added this
        objectHistory.push(object);  // added this
        res += "[";
        for(var prop in object) {
            if(prop == null) continue;
            if(typeof object[prop] === 'object') {
                stack.push(object[prop]);
            } else {
                res += prop + ": " +
                       object[prop].toString().replace(/[\t\r\n]/g, "") + ", ";
            }
        }
        res += "],";
    }
    return res;
}

var a = {
    b : {
        c : "hello",
        d : "world"
    },
    e : "alice",
    f : "bob",
    g : function() {
        console.log("hello");
    },
    h : [1, 2, 3]
};

a.x = {
  i: "I am X"
};
a.x.prototype = a;

console.log(getPropertyString(a));

Sign up to request clarification or add additional context in comments.

2 Comments

Simple. I like it!
Always satisfying to please with a simple solution :-) . If this answer is working for you, please remember to mark it as correct.
1

How about JSON.stringify it first and pass a function as the 2nd argument (so called 'replacer') in order to transform methods to strings too, because in default JSON.stringify discards functions. This way you will have all the properties and if you set an 'infinite loop' then JSON will alert you about it.

1 Comment

Well as I said, the method works for Object a (it also prints the method etc.). And I don't want an alert, but a function that takes care of that 'loop' case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.