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

Рекурсивно (или итеративно) создает вложенную таблицу html с d3.js?

У меня есть массив вложенных структур JSON, где они имеют различную глубину, а не один и тот же набор ключей:

[
    {
        "name":"bob",
        "salary":10000,
        "friends":[
            {
                "name": "sarah",
                "salary":10000
            },
            {
                "name": "bill",
                "salary":5000
            }
        ]
    },
    {
        "name":"marge",
        "salary":10000,
        "friends":[
            {
                "name": "rhonda",
                "salary":10000
            },
            {
                "name": "mike",
                "salary":5000,
                "hobbies":[
                    {
                        "name":"surfing",
                        "frequency":10
                    },
                    {
                        "name":"surfing",
                        "frequency":15
                    }
                ]
            }
        ]
    },
    {
        "name":"joe",
        "salary":10000,
        "friends":[
            {
                "name": "harry",
                "salary":10000
            },
            {
                "name": "sally",
                "salary":5000
            }
        ]
    }
]

Я хотел использовать D3 для рендеринга этого как вложенных html-таблиц. Например, столбец друзей будет иметь таблицы с указанием имени и оклада друзей человека, на которые делается ссылка в строке. Иногда одна из этих таблиц будет иметь другой уровень подкатегории.

Я предполагаю, что способ сделать это - рекурсивно создавать таблицы. Я написал программу python, которая принимает структуру JSON, как это, и отображает таблицы в таблицах, и самый простой способ сделать это был рекурсивно. Я вижу, что в документации d3.js есть .each() вещь, которую вы можете назвать, и я уверен, что мне нужно, мне просто нужно немного повысить (https://github.com/mbostock/d3/wiki/Selections#wiki-each).

Итак, есть ли хороший способ сделать это в D3? Я нашел этот отличный пример для рендеринга 2d-матрицы данных в виде таблицы Создание таблицы, связанной с файлом csv. С помощью этого учебника мне удалось получить внешний самый высокий уровень этой структуры данных, представленный в виде таблицы, но я зациклен на том, как переходить на уровни рекурсивно по мере необходимости, поскольку теперь они просто отображаются как "Объект" в таблице так как я не рассматриваю их иначе, чем обычные строки и числа.

Также я нашел этот другой вопрос/ответ, похожий на мой вопрос, но я действительно недостаточно разбираюсь в javascript, чтобы узнать, где и как происходит рекурсия, и обновил решение, соответствующее моим потребностям: Как обрабатывать данные, вложенные в несколько уровней в D3?. Любые советы или указатели на учебники по рекурсивной или итеративной обработке вложенного дерева, такие как JSON-структуры данных в D3, были бы очень оценены!

4b9b3361

Ответ 1

Рекурсивная функция, вероятно, будет хорошим подходом. См. Приведенный ниже код для одной возможной реализации (если ваши данные хранятся в jdata). См. Комментарии в коде для некоторого объяснения и посмотрите этот Gist для живой версии: http://bl.ocks.org/4085017

d3.select("body").selectAll("table")
    .data([jdata])
  .enter().append("table")
    .call(recurse);

function recurse(sel) {
  // sel is a d3.selection of one or more empty tables
  sel.each(function(d) {
    // d is an array of objects
    var colnames,
        tds,
        table = d3.select(this);

    // obtain column names by gathering unique key names in all 1st level objects
    // following method emulates a set by using the keys of a d3.map()
    colnames = d                                                     // array of objects
        .reduce(function(p,c) { return p.concat(d3.keys(c)); }, [])  // array with all keynames
        .reduce(function(p,c) { return (p.set(c,0), p); }, d3.map()) // map with unique keynames as keys
        .keys();                                                     // array with unique keynames (arb. order)

    // colnames array is in arbitrary order
    // sort colnames here if required

    // create header row using standard 1D data join and enter()
    table.append("thead").append("tr").selectAll("th")
        .data(colnames)
      .enter().append("th")
        .text(function(d) { return d; });

    // create the table cells by using nested 2D data join and enter()
    // see also http://bost.ocks.org/mike/nest/
    tds = table.append("tbody").selectAll("tr")
        .data(d)                            // each row gets one object
      .enter().append("tr").selectAll("td")
        .data(function(d) {                 // each cell gets one value
          return colnames.map(function(k) { // for each colname (i.e. key) find the corresponding value
            return d[k] || "";              // use empty string if key doesn't exist for that object
          });
        })
      .enter().append("td");

    // cell contents depends on the data bound to the cell
    // fill with text if data is not an Array  
    tds.filter(function(d) { return !(d instanceof Array); })
        .text(function(d) { return d; });
    // fill with a new table if data is an Array
    tds.filter(function(d) { return (d instanceof Array); })
        .append("table")
        .call(recurse);
  });    
}