TL; DR;
Use the function of the argument "key" Selection.data.
A key function can be specified to control which data item is assigned to which item "This function is evaluated for each selected item, in order, [...] The key function is then also evaluated for each new data item, [...] returned The string is a data key.
(Search for "If the key function is not specified ...")
More details ...
D3 binds data to elements. By default, the join-by-index method is used (the first element found is mapped to a datum with index 0, etc.). If joining by index is not enough for proper identification of elements, you should use the Selection.data key so that D3 properly synchronizes the input data set at the rendering stages: update (the result of calling the data () method), creating a selection input, "delete" (exit the selection).
Let's say we need to draw a legend, given the following data set ...
const dataSet = [ [ 15, "#440154" ] [ 238.58, "#414487" ]
first we decided to draw a colored square using the <rect> element.
const updateSelection = d3.select('rect').data(dataSet); // join-by-index strategy is enough here so there is no need to supply the key fn arg // Key function could be 'd => d[0]' (given data source cannot produce two entries // with value at index 0 being the same, uniqness is garanteed) updateSelection.enter() .append('rect') .attr('width', 15).attr('height', 15) .merge(updateSelection) .style('fill', d => d[1]) ; // Dispose outdated elements updateSelection.exit().remove();
Now we need to draw a <text> element for each given data element to visually represent the numerical values. Saving the connection strategy by index (by default) will force D3 to draw both rectangular and text elements first, but since only one element can be attached to a specific data key, only the first element found will be taken into account during the update phase, and any other element having the same data key will be at the end of the selection and will be removed from the DOM as soon as the Selection removal method is called.
... If several elements have the same key, duplicate elements are placed in the output selection ...
The solution is to combine the numeric value with the nodeName element.
// Notice how the selector has changed const updateSelection = d3.select('rect, text').data(dataSet, function(d) { // Concatenate the numeric value with the element nodeName return '${d[0]}:${this.nodeName}'; }) , enterSelection = updateSelection.enter() ; // Add rect element enterSelection .append('rect').attr('width', 15).attr('height', 15) .merge(updateSelection) .style('fill', d => d[1]) ; // Add text label enterSelection .append('text') .merge(updateSelection) .text(d => d[0]) ; // Garbage collection updateSelection.exit().remove();