Подтвердить что ты не робот

D3.js v4: Как получить доступ к исходному индексу родительской группы?

Описание функции selection.data включает пример с несколькими группами (ссылка), где двумерный массив превращается в таблицу HTML.

В d3.js v3 для более низких измерений функции accessor включали третий аргумент, который был индексом исходной группы родительской группы:

td.text(function(d,i,j) {
  return "Row: " + j;
});

В v4 этот аргумент j был заменен выбором NodeList. Как мне получить доступ к индексу данных исходной группы сейчас?

4b9b3361

Ответ 1

Ну, иногда ответ не дает решения, потому что решение может не существовать. Кажется, это так.

Согласно Бостоку:

Ive объединил новую реализацию выбора двухуровневого уровня в master, а также упростил, как отслеживаются родители с помощью параллельного массива parent.

Хорошим свойством этого нового подхода является то, что selection.data может оцените функцию значений точно так же, как и другие функции выбора: функция значений передается {d, i, узлы} где this является родительским node, d является родительским значением, я является родительским (группа), а узлы - это массив родительских узлов (по одному на группу). Кроме того, массив родителей может быть повторно использован подвыборами, которые не перегруппируйте выделение, например selection.select, так как родители массив неизменен.

Это изменение ограничивает функциональность - в том смысле, что вы не можетедоступ к родительскому объекту node из функции выбора, а также родительские данные, и индекс группы, но я считаю, что это, в конечном счете, A Хорошая вещь, потому что она поощряет более простой код.

(акцент мой)

Здесь ссылка: https://github.com/d3/d3-selection/issues/47

Таким образом, невозможно получить индекс родительской группы с помощью selection (индекс родительской группы можно получить с помощью selection.data, как показано ниже в этом фрагменте).

var testData = [
[
  {x: 1, y: 40},
  {x: 2, y: 43},
  {x: 3, y: 12},
  {x: 6, y: 23}
], [
  {x: 1, y: 12},
  {x: 4, y: 18},
  {x: 5, y: 73},
  {x: 6, y: 27}
], [
  {x: 1, y: 60},
  {x: 2, y: 49},
  {x: 3, y: 16},
  {x: 6, y: 20}
 ] 
];

var svg = d3.select("body")
	.append("svg")
  .attr("width", 300)
  .attr("height", 300);
  
var g = svg.selectAll(".groups")
    .data(testData)
    .enter()
    .append("g");
    
var rects = g.selectAll("rect")
    .data(function(d, i , j) { console.log("Data: " + JSON.stringify(d), "\nIndex: " + JSON.stringify(i), "\nNode: " + JSON.stringify(j)); return d})
    .enter()
    .append("rect");
<script src="https://d3js.org/d3.v4.min.js"></script>

Ответ 2

В итоге я определил внешнюю переменную "j", а затем увеличил ее, когда "i" равен 0

Пример V3 ниже.

rowcols.enter().append("rect")
 .attr("x", function (d, i, j) { return CalcXPos(d, j); })
 .attr("fill", function (d, i, j) { return GetColor(d, j); })

и V4, код преобразуется, как показано ниже.

var j = -1;
rowcols.enter().append("rect") 
 .attr("x", function (d, i) { if (i == 0) { j++ }; return CalcXPos(d, j); })
 .attr("fill", function (d, i) { return GetColor(d, j); })

Ответ 3

Если j - nodeList...

  • j[i] - текущий node (например, элемент td),
  • j[i].parentNode является родителем level-1 (например, элементом строки),
  • j[i].parentNode.parentNode является родителем level-2 (например, элементом таблицы),

  • j[i].parentNode.parentNode.childNodes - массив родителей уровня 1 (например, массив элементов строки), включая исходный родитель.

Итак, вопрос в том, каков индекс родителя (строки) по отношению к нему родительский (таблица)?

Мы можем найти это, используя Array.prototype.indexOf, как это...

k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);

В нижеприведенном фрагменте вы можете увидеть, что строка печатается в каждой ячейке td, когда возвращается k.

var testData = [
[
  {x: 1, y: 1},
  {x: 1, y: 2},
  {x: 1, y: 3},
  {x: 1, y: 4}
], [
  {x: 2, y: 1},
  {x: 2, y: 2},
  {x: 2, y: 3},
  {x: 2, y: 4}
], [
  {x: 3, y: 4},
  {x: 3, y: 4},
  {x: 3, y: 4},
  {x: 3, y: 4}
 ]
];

var tableData =
  d3.select('body').selectAll('table')
    .data([testData]);

var tables =
  tableData.enter()
  .append('table');

var rowData =
  tables.selectAll('table')
    .data(function(d,i,j){
      return d;
    });

var rows =
  rowData.enter()
  .append('tr');

var eleData =
  rows.selectAll('tr')
    .data(function(d,i,j){
      return d;
    });

var ele =
  eleData.enter()
  .append('td')
    .text(function(d,i,j){
      var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
      return k;
    });
<script src="https://d3js.org/d3.v4.min.js"></script>

Ответ 4

Вот пример использования метода selection.each(). Я не думаю, что это беспорядочно, но это замедляло рендеринг на большой матрице. Обратите внимание, что следующий код предполагает существующий выбор table и вызов update().

update(matrix) {
        var self = this;
        var tr = table.selectAll("tr").data(matrix);

        tr.exit().remove();

        tr.enter().append("tr");

        tr.each(addCells);

        function addCells(data, rowIndex) {
            var td = d3.select(this).selectAll("td")
                .data(function (d) {
                    return d;
                });
            td.exit().remove();

            td.enter().append("td");

            td.attr("class", function (d) {
                return d === 0 ? "dead" : "alive";
            });

            td.on("click", function(d,i){
                matrix[rowIndex][i] = d === 1 ? 0 : 1; // rowIndex now available for use in callback.                   
            });
        }

        setTimeout(function() {
            update(getNewMatrix(matrix))
        }, 1000);
    },

Ответ 5

Предположим, вы хотите сделать вложенную селекцию, и данные - это некоторый массив, в котором каждый элемент в свою очередь содержит массив, пусть говорят "значения". Затем вы вероятно, есть такой код:

var aInnerSelection = oSelection.selectAll(".someClass") //
    .data(d.values) //
    ...

Вы можете заменить массив на значения новым массивом, где вы кешируете индексы внутри группы.

var aInnerSelection = oSelection.selectAll(".someClass") //
    .data(function (d, i) {
        var aData = d.values.map(function mapValuesToIndexedValues(elem, index) {
                return {
                    outerIndex: i,
                    innerIndex: index,
                    datum: elem
                };
            })
            return aData;
    }, function (d, i) {
        return d.innerIndex;
    }) //
    ...

Предположим, что внешний массив выглядит следующим образом: [{name "X", values: [ "A", "B" ]}, {name "y", values: [ "C", "D" ]}
При первом подходе вложенный выбор приведет вас сюда

                  d                                i
------------------------------------------------------------------
root dummy X      {name "X", values: ["A", "B"]}    0
     dummy Y      {name "Y", values: ["C", "D"]}    1

здесь.

             d       i
------------------------------------------------------------------
root X  A    "A"     0
        B    "B"     1
     Y  C    "C"     2
        D    "D"     3

С расширенным массивом вы здесь:

             d                                              i
------------------------------------------------------------------
root X  A    {datum: "A", outerIndex: 0, innerIndex: 0}     0
        B    {datum: "B", outerIndex: 0, innerIndex: 1}     1
     Y  C    {datum: "C", outerIndex: 1, innerIndex: 0}     2
        D    {datum: "D", outerIndex: 1, innerIndex: 1}     3

Итак, у вас есть внутри вложенных выборов, в любой функции (d, i), все необходимую вам информацию.

Ответ 6

Мое обходное решение несколько похоже на Dinesh Rajan, предполагая, что родительский индекс необходим для атрибута someAttr of g.nestedElt:

v3

svg.selectAll(".someClass")
    .data(nestedData)
  .enter()
  .append("g")
    .attr("class", "someClass")
  .selectAll(".nestedElt")
    .data(Object)
  .enter()
  .append("g")
    .attr("class", "nestedElt")
    .attr("someAttr", function(d, i, j) {

    });

v4

svg.selectAll(".someClass")
    .data(nestedData)
  .enter()
  .append("g")
    .attr("class", "someClass")
    .attr("data-index", function(d, i) { return i; }) // make parent index available from DOM
  .selectAll(".nestedElt")
    .data(Object)
  .enter()
  .append("g")
    .attr("class", "nestedElt")
    .attr("someAttr", function(d, i) {
      var j = +this.parentNode.getAttribute("data-index");
    });