2

I'm writing a simple PrettyPrint function that only traverses shallowly through strings and objects.

I think it's pretty close

it's just

  {
    "name": "Jon",
    "facts": {
      "car": "Ford",
      "address": {
        "city": "New York"
      },
      "watch": "Casio",
      "other": {}
    },

 }

the }, and the space after that before the closing bracket, how do I fix that so it outputs as if JSON.Stringify would?

  {
    "name": "Jon",
    "facts": {
      "car": "Ford",
      "address": {
        "city": "New York"
      },
      "watch": "Casio",
      "other": {}
    }
 }

const exampleJson = {"name":"Jon","facts":{"car":"Ford","address":{"city":"New York"},"watch":"Casio","other": {}}};

const prettify = obj => {

  tabs = n => Array(n).fill(' ').join('');

  let traverse = (obj, tab = 1) => {
    let markup = '{\n';

    Object.entries(obj).forEach(kv => {
      const [key, val] = kv;
      if (typeof val === 'string') {
        const { length } = Object.keys(val);
        markup += `${tabs(tab)} "${key}": "${val}"`;
      } else if (typeof val === 'object') {
        const { length } = Object.keys(val);
        if (length > 0) {
          markup += `,\n${tabs(tab)} "${key}": ${traverse(val, tab+2)},\n`;
        } else {
          markup += `,\n${tabs(tab)} "${key}": {}`;
        }

      }
    })
    
    markup += `\n${tabs(tab - 1)}}`;
    return markup;
  }

  let results = traverse(obj);

  console.log(results);
}

prettify(exampleJson);
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
totalnoob
  • 2,521
  • 8
  • 35
  • 69
  • 3
    Possible duplicate of [How can I pretty-print JSON using JavaScript?](https://stackoverflow.com/questions/4810841/how-can-i-pretty-print-json-using-javascript) – imvain2 Mar 05 '19 at 20:37
  • 2
    @imvain2 I'm trying to do this without JSON.Stringify. it's a general recursion question – totalnoob Mar 05 '19 at 20:39
  • 1
    In the line below `if(length > 0)` there is a `,\n` that I do not understand in the template literal – Jonas Wilms Mar 05 '19 at 20:40
  • In general, when you need to make a comma separated list of stuff, rather than starting with an empty string and appending each element plus a comma to it, push each element into an array and then join the array to a string with commas as the delimiter. That will prevent the "final comma" problem. – James Mar 05 '19 at 20:43
  • @JonasWilms, without it, the ending for address's } would not have a , and would be on the same line as watch – totalnoob Mar 05 '19 at 20:43
  • @JonasWilms It's adding a comma and newline after the last property (if you look closely, no commas are added after each property) – Jack Bashford Mar 05 '19 at 20:46
  • @totalnoob yes, but that's another mistake: `{ a: "test", b: "test" }` wouldn't add a comma too as you do not add commas to strings. You always want to prepend a comma instead for the first k-v pair. – Jonas Wilms Mar 05 '19 at 20:46
  • Just wanted to add a link to a great site that does exactly this: https://beautifier.io/ – Steven Stark Mar 05 '19 at 21:26

2 Answers2

2

You add a comma in this case but not in the other, in one case you prepend it in another you append it in another you do both. That will never work in the general case.

Instead you should use a comma and a newline regardless of the value. Either prepend it to anything except the first

 Object.entries(obj).forEach(([key, value], i) => {
   if(i) markup += ",\n";
   markup += tabs(tab);
   markup += key + ": ";
   // serialize and add value
 });

Or append it to anything except the last:

Object.entries(obj).forEach(([key, value], i, { length }) => {
   markup += tabs(tab);
   markup += key + ": ";
   // serialize and add value
   if(i !== length - 1) markup += ",\n";
});
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
1

You can solve both those problems at once by replaceing some characters:

const exampleJson = {"name":"Jon","facts":{"car":"Ford","address":{"city":"New York"},"watch":"Casio","other": {}}};

const prettify = obj => {

  tabs = n => Array(n).fill(' ').join('');

  let traverse = (obj, tab = 1) => {
    let markup = '{\n';

    Object.entries(obj).forEach(kv => {
      const [key, val] = kv;
      if (typeof val === 'string') {
        const { length } = Object.keys(val);
        markup += `${tabs(tab)} "${key}": "${val}"`;
      } else if (typeof val === 'object') {
        const { length } = Object.keys(val);
        if (length > 0) {
          markup += `,\n${tabs(tab)} "${key}": ${traverse(val, tab+2)},\n`;
        } else {
          markup += `,\n${tabs(tab)} "${key}": {}`;
        }

      }
    })
    
    markup += `\n${tabs(tab - 1)}}`;
    return markup;
  }

  let results = traverse(obj);

  console.log(results.replace("\,\n\n", "\n"));
}

prettify(exampleJson);
.as-console-wrapper { max-height: 100% !important; top: auto; }
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79