2012-11-12 9 views
12

Ho una matrice di strutture nidificate JSON dove hanno diversa profondità e non lo stesso insieme di chiavi dappertutto:In modo ricorsivo (o iterativamente) crea una tabella hid nidificata con d3.js?

[ 
    { 
     "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 
      } 
     ] 
    } 
] 

ho voluto usare D3 per rendere questo tabelle html come nidificate. Ad esempio la colonna di amici avrà tabelle che mostrano il nome e lo stipendio degli amici dell'individuo cui fa riferimento nella riga. A volte una di queste tabelle avrà un altro livello di un sotto-tavolo.

Immagino che il modo per farlo sia creando ricorsivamente tabelle. Ho scritto un programma python che prende una struttura JSON come questa, e rende tabelle all'interno di tabelle, e il modo più semplice per farlo è ricorsivamente. Vedo sulla documentazione di d3.js c'è una cosa .each() che puoi chiamare, che sono sicuro è quello di cui ho bisogno, ho solo bisogno di una piccola spinta per arrivarci (https://github.com/mbostock/d3/wiki/Selections#wiki-each).

Quindi c'è un bel modo per farlo in D3? Ho trovato questo ottimo esempio per il rendering di una matrice di dati 2d come tabella Creating a table linked to a csv file. Con quel tutorial sono stato in grado di ottenere il livello più esterno di questa struttura di dati resa come una tabella, ma sono bloccato su come accedere ai livelli in modo ricorsivo secondo necessità, poiché ora appaiono semplicemente come "Oggetto" nella tabella dal momento che non li sto trattando in modo diverso dalle stringhe e dai numeri normali.

Inoltre ho trovato questa altra domanda/risposta che è simile alla mia domanda, ma davvero non capisco javascript abbastanza bene per vedere dove/come sta accadendo la ricorsione e riadattare la soluzione per soddisfare le mie esigenze: How do I process data that is nested multiple levels in D3?. Qualunque consiglio o suggerimento di esercitazioni su ricorsivamente o iterativamente l'elaborazione di strutture nidificate come le strutture dati JSON in D3 sarebbe molto apprezzato!

risposta

18

Una funzione ricorsiva sarebbe probabilmente un buon approccio. Vedere il codice sottostante per una possibile implementazione (supponendo che i dati siano memorizzati in jdata). Vedere i commenti nel codice per qualche spiegazione e vedere questo Gist per una versione live: 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); 
    });  
} 
+0

contiene caratteri illegali nel file javascript! ho davvero bisogno di sostituire queste variabili in quasi 8000 righe? veramente? wtf – msqar

+3

@msqar - no, devi solo specificare che è UTF-8: vedi https://github.com/mbostock/d3/wiki/Upgrading-to-3.0. Hai bisogno di un doctype, un tag e lo script deve essere incluso in questo modo: dja

+0

Sembra fantastico. Che dire dell'array di array? @nautat – bumpkin