General Update Pattern with nested data hierarchy

71 views
Skip to first unread message

Joonas Harjukallio

unread,
Apr 25, 2018, 10:58:57 AM4/25/18
to d3-js
Hi,

TL;DR I want this to behave similar to this

I'm trying to understand how general update pattern can be used on deep hierarchical data. I want to have quite similar behaviour compared to General update pattern III.

In the example input is flat array of characters and output is svg with text elements. Working fiddle can be found here.

In my example (jsfiddle can be found here) I want to have more deep data structure like this:
const alphabet = [
  { characters: [{ char: 'a' }, { char: 'b' }] },
  { characters: [{ char: 'c' }, { char: 'd' }] }
];

and the output should be something like this:
<svg>
  <g transform="translate(0, 10)">
    <g transform="translate(0,0)">
      <text fill="green" x="0" dy=".35em" y="0">
        b
      </text>
    </g>
    <g transform="translate(32,0)">
      <text fill="green" x="0" dy=".35em" y="0">
        d
      </text>
    </g>
  </g>
</svg>

There is a few things I don't quite understand. The expected exit selections should be only a and c but it is actually ab and cd. I think my data bindings key function is to blame but I don't understand what I should put there. Now it is:
const groups = select('svg')
  .select('g')
  .selectAll('g')
  .data(data, d => JSON.stringify(d));

Also the update selection is always empty. Is this due to wrong key function too?

Thanks for your help!
Joonas

Joonas Harjukallio

unread,
Apr 26, 2018, 3:43:34 AM4/26/18
to d3-js
Now that I thought this problem again using JSON.stringfy(d) as key function was just silly. I have pretty good working solution here but the problem is that it's only working with d3 v3.x and not at all with v4.x. Any thoughts why this is so? (I also made the data structure a bit more simple for testing purposes)

Latest working code:

const a = 'a';
const b = 'b';
const c = 'c';
const d = 'd';

const alphabet = [[a, b], [c, d]];
const alphabet2 = [[b], [d]];

update(alphabet);

setTimeout(() => {
  update(alphabet2);
}, 1000);

function update(data) {
  const t = d3.transition().duration(1000);

  const groups = d3.select('svg')

    .select('g')
    .selectAll('g')
    .data(data);

  groups
    .exit()
    .transition(t)
    .remove();
  groups.transition(t).attr('transform', (d, i) => `translate(${i * 32},0)`);

  groups
    .enter()
    .append('g')
    .transition(t)
    .attr('transform', (d, i) => `translate(${i * 32},0)`);

  const texts = groups.selectAll('text').data(d => d, d => d);

  texts
    .exit()
    .transition(t)
    .attr('fill', 'brown')
    .attr('y', 60)
    .style('fill-opacity', 1e-6)
    .remove();

  texts
    .attr('fill', 'black')
    .attr('y', 0)
    .style('fill-opacity', 1)
    .transition(t)
    .attr('x', (d, i) => i * 10);

  texts
    .enter()
    .append('text')
    .attr('fill', 'green')
    .attr('dy', '.35em')
    .attr('y', -60)
    .attr('x', (d, i) => i * 10)
    .style('fill-opacity', 1e-6)
    .text(d => d)
    .transition(t)
    .attr('y', 0)
    .style('fill-opacity', 1);
}

Joonas Harjukallio

unread,
Apr 27, 2018, 4:11:13 AM4/27/18
to d3-js
Ok I was able to figure this out. Based on this in v4 you need to have merge call on nested selection. Working fiddle can be found here


const a = 'a';
const b = 'b';
const c = 'c';
const d = 'd';


const alphabet = [[a, b], [c, d]];
const alphabet2 = [[b], [d]];

update(alphabet);

setTimeout(() => {
update(alphabet2);
}, 1000);

function update(data) {
const t = d3.transition().duration(1000);

  const groups = d3.select('svg')

.select('g')
.selectAll('g')
    .data(data);

groups
.exit()
.transition(t)
.remove();
groups.transition(t).attr('transform', (d, i) => `translate(${i * 32},0)`);

  const groupsEnter = groups

.enter()
.append('g')
.transition(t)
.attr('transform', (d, i) => `translate(${i * 32},0)`);

  const texts = groups.merge(groupsEnter).selectAll('text').data(d => d, d => d);

Drew Winget

unread,
Apr 27, 2018, 1:36:42 PM4/27/18
to d3...@googlegroups.com
Joonas, I regret that I didn't see this earlier, as I would have loved to help. Nevertheless, I wanted to thank you for this very well-written and considerate help request, that included a cogent description with running, minimal examples and formatted code - and for posting the solution! This hasn't always been the norm on this list lately.

-Drew

--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages