Nested selections

I have a 2-D array from which I want to create a series of non-nested rect elements. It’s similar to the 2-D selection example from the docs, except I don’t want the elements to be nested. Here is a sample of my closest working attempt where I use the same data in the example and try to create a single ordered list from it – with the twist that I only want the first two elements from each sub-array.

Data from example that I’m using:

matrix = [
  [11975,  5871, 8916, 2868],
  [ 1951, 10048, 2060, 6171],
  [ 8010, 16145, 8090, 8045],
  [ 1013,   990,  940, 6907]
];

Cell Definition:

{
  const ol = d3.create('ol');
  ol.selectAll('li').data(matrix)
    .join('li')          // Each <li> has its own sub-array.
    .selectAll('li')     // Selects children of <li>, not the existing <li>
    .data(d => d.slice(0, 2))  // I only want the first two elements of each sub-array.
    .join('li')          // Creates children <li> of parent <li>
    .text((d, i, p) => `${i+1}: ${d}`)  // Docs tell me p is the "parent".  How do I use it?
  return ol.node()
}

It renders HTML structured as (according to browser inspector):

<ol>
  <li>
    <li> 1: 1195</li>
    <li> 2: 5871</li>
  </li>
  <li>
    <li> 1: 1951</li>
    <li> 2: 10048</li>
  </li>
  ... etc ...
</ol>

But what I want is to remove the nesting (i.e. just <ol> with children <li>) so that I see

<ol>
    <li> 1: 1195</li>
    <li> 2: 5871</li>
    <li> 1: 1951</li>
    <li> 2: 10048</li>
  ... etc ...
</ol>

I’m stuck in the position that my selection only allows me to append children to the <li> element. I don’t know how to append siblings.

In the case above where the array is simply numbers, I could just flatten the 2-D array before processing. In my actual case, it’s more complicated. I wish to handle each group individually. In my current implementation, to use Mike’s phrase in his join article, I “bust out nested for-loops”.

You’ll need to follow your intuition and flatten the matrix before joining; add in any information you need for the “more complicated case”.

Check out flatMap():

{
  const ol = d3.create('ol');
  ol.selectAll('li').data(matrix.flatMap(d => [d[0], d[1]]))
    .join('li')
    .text((d, i) => `${i%2+1}: ${d}`)
  return ol.node()
}

produces:

<ol>
<li>1: 11975</li>
<li>2: 5871</li>
<li>1: 1951</li>
<li>2: 10048</li>
<li>1: 8010</li>
<li>2: 16145</li>
<li>1: 1013</li>
<li>2: 990</li>
</ol>
1 Like