javascriptd3.jsvisualizationdiagram

In a diagram, relations are always deleted from last to first instead of selected


I have a basic diagram in D3.js, containing objects and relations between objects initialized in these arrays:

let objectsData = [
  { id: 1, x: 150, y: 150, type: "circle", color: "#ff6b6b" },
  { id: 2, x: 300, y: 200, type: "rect", color: "#4ecdc4" },
  { id: 3, x: 500, y: 150, type: "circle", color: "#45b7d1" },
  { id: 4, x: 400, y: 350, type: "rect", color: "#96ceb4" },
  { id: 5, x: 200, y: 400, type: "circle", color: "#feca57" },
];

let relationsData = [
  { source: 1, target: 2 },
  { source: 2, target: 3 },
  { source: 3, target: 4 },
  { source: 4, target: 5 },
];

When my updateRelations() method is first called, everything is displayed correctly, but as I click relations to remove them, they are always removed from last to first in the relationsData table, it's never the one that's been clicked that is removed:

const relations = svg.append("g").attr("class", "relations");

function updateRelations() {
  const relationGroups = relations.selectAll(".relation-group").data(relationsData);

  const enterGroups = relationGroups
  .enter()
  .append("g")
  .attr("class", "relation-group")
  .style("cursor", "pointer")
  .on("click", function (event, d) {
    event.stopPropagation();
    const index = relationsData.indexOf(d);
    if (index > -1) {
      relationsData.splice(index, 1);
      updateRelations();
    }
  });

  // Add visible line
  enterGroups
    .append("line")
    .attr("class", "relation");

  const allGroups = enterGroups.merge(relationGroups);

  allGroups.selectAll(".relation")
  .attr("x1", (d) => getObjectById(d.source).x)
  .attr("y1", (d) => getObjectById(d.source).y)
  .attr("x2", (d) => getObjectById(d.target).x)
  .attr("y2", (d) => getObjectById(d.target).y);

  relationGroups.exit().remove();
}

function getObjectById(id) {
  return objectsData.find((obj) => obj.id === id);
}

At first I thought it was my index variable that was miscalculated (like : it would always be -1), but its value seems correct.


Solution

  • You could use findIndex to match by property.

    // Add unique id
    let relationsData = [
      { id: 'rel1', source: 1, target: 2 },
      { id: 'rel2', source: 2, target: 3 },
      { id: 'rel3', source: 3, target: 4 },
      { id: 'rel4', source: 4, target: 5 },
    ];
    
    function updateRelations() {
      // Use key function with id 
      const relationGroups = relations.selectAll(".relation-group").data(relationsData, d => d.id);
      // ...
    
      const enterGroups = relationGroups
        .enter()
        .append("g")
        .attr("class", "relation-group")
        .style("cursor", "pointer")
        .on("click", function (event, d) {
          event.stopPropagation();
          
          // Use findIndex to match
          const index = relationsData.findIndex(rel => rel.id === d.id);
          if (index > -1) {
            relationsData.splice(index, 1);
            updateRelations();
          }
        });
      // ...
    }