Stack Exchange
JSON.stringify Map with nested Set
Consider the following graph:
class Node {
  constructor(value) {
    this.value = value;
    this.adjacents = new Set();
  }

  addAdjacent(node) {
    this.adjacents.add(node);
  }
}

class Graph {
  constructor(directed = false) {
    this.nodes = new Map();
    this.directed = directed;
  }

  addVertex(value) {
    const node = this.nodes.has(value);
    if (node) {
      return this.nodes.get(value);
    }

    const vertex = new Node(value);
    this.nodes.set(value, vertex);
    return vertex;
  }

  addEdge(src, dest) {
    let srcNode = this.nodes.get(src);
    if (!srcNode) {
      srcNode = this.addVertex(src);
    }

    let destNode = this.nodes.get(dest);
    if (!destNode) {
      destNode = this.addVertex(dest);
    }

    srcNode.addAdjacent(destNode);
    if (this.directed === false) {
      destNode.addAdjacent(srcNode);
    }
  }
}

const g1 = new Graph();
g1.addVertex("a");
g1.addVertex("b");
g1.addEdge("a", "c");

When I console.log the graph object g1. I get the following output:
Graph {
  nodes: Map(3) {
    'a' => Node { value: 'a', adjacents: [Set] },
    'b' => Node { value: 'b', adjacents: Set(0) {} },
    'c' => Node { value: 'c', adjacents: [Set] }
  },
  directed: false
}

As output shows, I get no info regarding the edges of the vertices.
I tried passing replacer to JSON.stringify as follows, but it results in StackOverFlow error.
console.log(
  JSON.stringify(
    Object.fromEntries(g1.nodes),
    (_key, value) =>
      value.adjacents instanceof Set ? [...value.adjacents] : value,
    2
  )
);

Error:
  JSON.stringify(
       ^

RangeError: Maximum call stack size exceeded
    at JSON.stringify (<anonymous>)

How can I print the graph in a more friendly format?

@Philisonstrike then how can I print it a more friendly format?

There's two issues here...

Both Map and Set do not serialise directly to JSON
Your data structure is cyclic and therefore cannot be serialised; node a is adjacent to node c and node c is adjacent to node a. Attempting to serialise this to a flat structure would require infinite storage.

I would recommend adding a toJSON() method to your classes to aid in the serialisation. There, unserialisable data structures can be converted to simple ones and cyclic references can be broken.
For example


class Node {
  constructor(value) {
    this.value = value;
    this.adjacents = new Set();
  }

  addAdjacent(node) {
    this.adjacents.add(node);
  }

  toJSON() {
    return {
      ...this,
      // plain array with scalar values
      adjacents: [...this.adjacents].map(({ value }) => value),
    };
  }
}

class Graph {
  constructor(directed = false) {
    this.nodes = new Map();
    this.directed = directed;
  }

  addVertex(value) {
    const node = this.nodes.has(value);
    if (node) {
      return this.nodes.get(value);
    }

    const vertex = new Node(value);
    this.nodes.set(value, vertex);
    return vertex;
  }

  addEdge(src, dest) {
    let srcNode = this.nodes.get(src);
    if (!srcNode) {
      srcNode = this.addVertex(src);
    }

    let destNode = this.nodes.get(dest);
    if (!destNode) {
      destNode = this.addVertex(dest);
    }

    srcNode.addAdjacent(destNode);
    if (this.directed === false) {
      destNode.addAdjacent(srcNode);
    }
  }

  toJSON() {
    return {
      ...this,
      // reduce the Map entries to a simple object
      nodes: Object.fromEntries(this.nodes),
    };
  }
}

const g1 = new Graph();
g1.addVertex("a");
g1.addVertex("b");
g1.addEdge("a", "c");

console.log(JSON.stringify(g1, null, 2));
.as-console-wrapper { max-height: 100% !important; }



