Skip to main content
Loading...

More JavaScript Posts

class Graph {
  constructor(directed = true) {
    this.directed = directed;
    this.nodes = [];
    this.edges = new Map();
  }

  addNode(key, value = key) {
    this.nodes.push({ key, value });
  }

  addEdge(a, b, weight) {
    this.edges.set(JSON.stringify([a, b]), { a, b, weight });
    if (!this.directed)
      this.edges.set(JSON.stringify([b, a]), { a: b, b: a, weight });
  }

  removeNode(key) {
    this.nodes = this.nodes.filter(n => n.key !== key);
    [...this.edges.values()].forEach(({ a, b }) => {
      if (a === key || b === key) this.edges.delete(JSON.stringify([a, b]));
    });
  }

  removeEdge(a, b) {
    this.edges.delete(JSON.stringify([a, b]));
    if (!this.directed) this.edges.delete(JSON.stringify([b, a]));
  }

  findNode(key) {
    return this.nodes.find(x => x.key === key);
  }

  hasEdge(a, b) {
    return this.edges.has(JSON.stringify([a, b]));
  }

  setEdgeWeight(a, b, weight) {
    this.edges.set(JSON.stringify([a, b]), { a, b, weight });
    if (!this.directed)
      this.edges.set(JSON.stringify([b, a]), { a: b, b: a, weight });
  }

  getEdgeWeight(a, b) {
    return this.edges.get(JSON.stringify([a, b])).weight;
  }

  adjacent(key) {
    return [...this.edges.values()].reduce((acc, { a, b }) => {
      if (a === key) acc.push(b);
      return acc;
    }, []);
  }

  indegree(key) {
    return [...this.edges.values()].reduce((acc, { a, b }) => {
      if (b === key) acc++;
      return acc;
    }, 0);
  }

  outdegree(key) {
    return [...this.edges.values()].reduce((acc, { a, b }) => {
      if (a === key) acc++;
      return acc;
    }, 0);
  }
}


const g = new Graph();

g.addNode('a');
g.addNode('b');
g.addNode('c');
g.addNode('d');

g.addEdge('a', 'c');
g.addEdge('b', 'c');
g.addEdge('c', 'b');
g.addEdge('d', 'a');

g.nodes.map(x => x.value);  // ['a', 'b', 'c', 'd']
[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['a => c', 'b => c', 'c => b', 'd => a']

g.adjacent('c');            // ['b']

g.indegree('c');            // 2
g.outdegree('c');           // 1

g.hasEdge('d', 'a');        // true
g.hasEdge('a', 'd');        // false

g.removeEdge('c', 'b');

[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['a => c', 'b => c', 'd => a']

g.removeNode('c');

g.nodes.map(x => x.value);  // ['a', 'b', 'd']
[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['d => a']

g.setEdgeWeight('d', 'a', 5);
g.getEdgeWeight('d', 'a');  // 5
const rectSize = 15; // Size of the color box
        const spacing = 5; // Space between legend items
        let legend = canvas.append('g').attr('class','chart-legend')
        .attr('transform', `translate(${0}, ${height + 3})`);

        // Sample data for the legend
        const legendData = [
            { label: 'low', color: '#1f77b4' },
            { label: 'medium', color: '#ff7f0e' },
            { label: 'high', color: '#2ca02c' },
            { label: 'very high', color: '#2ca02c' }
        ];


        legend.selectAll('g')
            .data(legendData)
            .join('g')
            .attr('class', 'legend-item')
            
            .each(function (d, i) {
                const legendItem = d3.select(this);
                console.log(d.label)
                console.log(d.label?.length)
                let labelWidth = (d.label.length * 5) + 7 + 15
                

                var bbox = legendItem.node().getBBox()
                var width = bbox.width
                console.log(width)
                
                // Add rectangle for color
                legendItem.append('rect')
                    .attr('width', rectSize)
                    .attr('height', rectSize)
                    .attr('fill', d.color);
                    
                    // Add label
                    legendItem.append('text')
                    .attr('x', rectSize + spacing) // Position text to the right of the rectangle
                    .attr('y', rectSize / 2) // Align text vertically with the rectangle
                    .attr('dy', '.35em') // Adjust vertical alignment
                    .attr('text-anchor', 'start') // Align text to the start
                    .text(d.label)
                    .attr('fill', 'black'); // Optional: Text color
                // legendItem.attr('transform', `translate(${i * (rectSize + spacing + labelWidth)}, 0)`) // Position each item horizontally
            });

            const legendItems_spacing = 12
            var legendLength = 0
            legend.selectAll('.legend-item').each(function(d,i) {
              d3.select(this).attr('transform',function() {
                var bbox = d3.select(this).node().getBBox()
                var width = bbox.width
                // if (width == 0) width = (d.text.length * 5) +15  // not in dom yet
                let startingPosition = 0 
                if (i > 0) {
                    startingPosition = legendLength
                }
                legendLength += width + legendItems_spacing
                
                // return `translate(${xx - (width + 30)},0$)`
                return `translate(${startingPosition},0)`
            })
            })