Address JS ES6 Map in arrow functions

Viewed 177

In Observable I have a Map called dataByHub, currently looks like:

"ST" => Array(19) [Object, Object, Object ...]
"FING" => Array(27) [Object, Object, Object ...]

I am trying to iterate over the key (Are ST, FING keys? Not sure of nomenclature...)

d3.select("#hubs").selectAll(".hub")
    .data(dataByHub) 
    .enter().append("g")
    .attr("class", "hub")
    .attr("id", (d,i) => d[i]) 

I want:

<g class="hub" id="ST"></g>
<g class="hub" id="FING"></g>

The result I get is:

<g class="hub" id="ST"></g>
<g class="hub" id="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"></g>

How do I get the result I want?

Bonus: How do I address the Objects in a key of the Map in a => to iterate over them and use the values of the objects?

2 Answers

You want the key (you were right, that's the correct nomenclature) of each key-value pair in the Map. Therefore, you just need to use the index 0 for each entry (which is the datum, d) for getting its key:

.attr("id", d => d[0]);

Here is the demo:.

const svg = d3.select("svg");
const dataByHub = new Map();
dataByHub.set("ST", [1, 2, 3, 4, 5]);
dataByHub.set("FING", [6, 7, 8, 9, 10]);
svg.selectAll(null)
  .data(dataByHub)
  .enter()
  .append("g")
  .attr("class", "hub")
  .attr("id", d => d[0]);
var mySVG = (new XMLSerializer()).serializeToString(svg.node());
console.log(mySVG)
<script src="https://d3js.org/d3.v6.min.js"></script>
<svg></svg>

As an additional explanation, it's worth mentioning that D3 data method uses Array.from internally for Maps, Sets and Strings. That's why each of your datum (d) is an array, with the original Map key as the element at index 0 and the original Map value as the element at index 1:

const dataByHub = new Map();
dataByHub.set("ST", [1, 2, 3, 4, 5]);
dataByHub.set("FING", [6, 7, 8, 9, 10]);
console.log(Array.from(dataByHub))

It's also worth mentioning that since v6 (we're in v7 now) D3 accepts iterables, like Maps and Sets.

You can use dataByHub.keys() to set the id of the g elements:

const data = [
  {hub: "ST", Xs: [1, 2]},
  {hub: "FING", Xs: [2, 3]},
  {hub: "ST", Xs: [3, 4]},
  {hub: "FING", Xs: [4, 5]},
  {hub: "ST", Xs: [5, 6]},
  {hub: "FING", Xs: [6, 7]},
  {hub: "ST", Xs: [7, 8]},
];

const dataByHub = d3.group(data, d => d.hub);

const svg = d3.select("svg");

const hubs = svg.selectAll(".hubs")
  .data(dataByHub.keys())
  .enter()
  .append("g")
  .attr("class", "hub")
  .attr("id", d => d)

hubs.append("rect")
  .attr("width", 160)
  .attr("height", 140)
  .attr("transform", (d, i) => `translate(${(i * 180) + 20}, 20)`);
  
hubs.append("text")
  .text(d => `I am rect in ${d} <g>`)
  .attr("x", (d, i) => (i * 180) + 25)
  .attr("y", 90)
  .attr("fill", "blue");
svg {
  background: lightblue;
}

g.hub rect {
  fill: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg width=380 height=180></svg>

Related