Search code examples
javascriptvis.jsvis.js-network

Can nodes in a hierarchichal layout be ordered with some function?


Given a directed graph, where a value is associated with each node, how can we use vis.js to display the nodes ordered according to this value? E.g., all nodes at same depth are displayed left to right from "most valuable" to "least valuable"


Solution

  • In the example below, nodes 5 through 8 are children of node 4 and are added to left to right, in that order due to the index of edges and not with respect to the value of each node.

    So we can rearrange edges to sort descending over the from node's value in nodes:

    function rank(edges) {
      // get a copy of edges
      let edges2 = edges.map(function(edge) {return Object.assign({}, edge);});
      // sort edges by node value
      edges2.sort(function(a, b) {
        let nodeA = nodes.find(function(n) {return n.id == a.from});
        let nodeB = nodes.find(function(n) {return n.id == b.from});
        return nodeB.value - nodeA.value;
      });
      return edges2;
    }
    

    Working example with both unranked, and the ranking per this function:

    const nodes = [
      {id: 1, level: 0, value: 1, label: "1"},
      {id: 2, level: 1, value: 1, label: "2"},
      {id: 3, level: 1, value: 2, label: "3"},
      {id: 4, level: 2, value: 1, label: "4"},
      {id: 5, level: 3, value: 1, label: "5"},
      {id: 6, level: 3, value: 2, label: "6"},
      {id: 7, level: 3, value: 3, label: "7"},
      {id: 8, level: 3, value: 4, label: "8"}
    ];
    
    const edges = [
      {from: 2, to: 1},
      {from: 3, to: 1},
      {from: 4, to: 3},
      {from: 5, to: 4},
      {from: 6, to: 4},
      {from: 7, to: 4},
      {from: 8, to: 4}
    ];
    
    const options = {
      nodes: {
        shape: "dot"
      },
      edges: {
        smooth: {
          type: "cubicBezier",
          forceDirection: "vertical",
          roundness: 0.4
        }
      },
      layout: {
        hierarchical: {
          direction: "UD",
        },
      },
      physics: false
    }
    
    // unranked
    const data1 = {
      nodes: nodes,
      edges: edges
    }
    const container1 = document.getElementById("network1");
    const network1 = new vis.Network(container1, data1, options);
    
    // ranked
    const data2 = {
      nodes: nodes,
      edges: rank(edges)
    }
    function rank(edges) {
      // get a copy of edges
      let edges2 = edges.map(function(edge) {return Object.assign({}, edge);});
      // sort edges by node value
      edges2.sort(function(a, b) {
        let nodeA = nodes.find(function(n) {return n.id == a.from});
        let nodeB = nodes.find(function(n) {return n.id == b.from});
        return nodeB.value - nodeA.value;
      });
      return edges2;
    }
    const container2 = document.getElementById("network2");
    const network2 = new vis.Network(container2, data2, options);
    #wrapper {
      display: flex;
    }
    
    #network1 {
      flex: 0 0 50%;
      height: 180px; 
      border: 1px solid #000;
    }
    
    #network2 {
      flex: 1;
      height: 180px; 
      border: 1px solid #000;
    }
    <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vis-network.min.js"></script>
    <div id="wrapper">
      <div id="network1"></div>
      <div id="network2"></div>
    </div>