Порядок хранения данных D3

Из того, что я понимаю, правильный способ обновить данные d3 - это передать его в выделение .data().

Данные, которые я получаю, не упорядочены или не совместимы. Поэтому мне очень важно использовать логику обновления/добавления/удаления. Таким образом, в d3-выражениях .data() с .enter() и .exit() (справа?).

Итак, я хотел бы использовать словарь javascript вместо массива, где ключ - это мой уникальный идентификатор. Но я не могу этого сделать. Поддельный пример:

data_one[0] = 'Dogs';
data_one[1] = 'Cats';

d3.selectAll('circle').data(data_one).enter().attr(...)

Во второй раз, когда я запускаю это, мои данные могут быть одинаковыми, но в другом порядке. Я хочу представить его с теми же атрибутами, что и раньше. Я не хочу дублировать свой код, но если я просто делаю .data(data_two), то неправильные круги обновляются с новыми данными.

Как это обойти?

Ответы

Ответ 1

Это назначение ключевой функции , второй аргумент selection.data: он позволяет вам поддерживать постоянство объекта, контролируя, какая привязка привязывается к какому элементу. Например, скажем, у вас был массив объектов, представляющих фрукты:

var fruits = [
  {name: "orange", value: 200},
  {name: "apple", value: 124},
  {name: "banana", value: 32}
];

Когда вы сначала создаете элементы, вам не нужна ключевая функция, потому что существуют любые существующие элементы, и поэтому вы можете использовать стандартный шаблон selectAll-data-enter-append:

var div = d3.select("body").selectAll(".fruit")
    .data(fruits)
  .enter().append("div")
    .attr("class", "fruit")
    .text(function(d) { return d.name + ": " + d.value; });

Результат этой операции:

<body>
  <div class="fruit">orange: 200</div>
  <div class="fruit">apple: 124</div>
  <div class="fruit">banana: 32</div>
</body>

Но теперь можно сказать, что ваши данные изменяются, и вы хотите обновить их, чтобы отразить новые данные. Эти новые данные могут быть в другом порядке, и некоторые элементы могут быть добавлены или удалены:

var fruits2 = [
  {name: "apple", value: 124},
  {name: "lemon", value: 17},
  {name: "banana", value: 32},
  {name: "strawberry", value: 1465}
];

Яблоко и банан были в исходных данных, и поэтому мы хотим их сохранить. Это называется выбором обновления. Клубника и лимон - новые; это выбор ввода. И, наконец, оранжевый ушел, образуя выбор выхода.

Используя ключевую функцию, которая ссылается на свойство name базы данных, мы можем назначить новые данные старым элементам по назначению. Затем мы можем создать входящий плод и удалить выходящий плод:

var div = d3.select("body").selectAll(".fruit")
    .data(fruits2, function(d) { return d.name; });

div.enter().append("div")
    .attr("class", "fruit")
    .text(function(d) { return d.name + ": " + d.value; });

div.exit().remove();

Это называется общим шаблоном обновления . (Вы могли бы сделать это в первый раз, это более общая форма шаблона selectAll-data-enter-append.) Результат:

<body>
  <div class="fruit">apple: 124</div>
  <div class="fruit">banana: 32</div>
  <div class="fruit">lemon: 17</div>
  <div class="fruit">strawberry: 1465</div>
</body>

Пока порядок выбора div соответствует данным fruit2, порядок в DOM не потому, что элементы ввода были добавлены в конец тела. (selection.append не имеет никакой логической логики, чтобы гарантировать заказ, он просто добавляет новые элементы в конец родителя.) При использовании SVG мы часто не заботимся о порядке элементов DOM, потому что все позиционируется абсолютно. Если вы позаботились о заказе, вы можете использовать selection.order, чтобы исправить его после ввода:

div.order(); // make the DOM element order match the selection

Кроме того, в этом примере нам не нужно вносить какие-либо изменения в выбор обновления, потому что значения для apple и banana не изменились. Если данные обновления также изменяются, вы можете совершать вызовы цепочки на selection.attr и selection.text непосредственно из вызова select.data выше.