2016-03-07 308 views
2

如何基於一系列嵌套值使用D3來平整表格?d3:拼合嵌套數據?

對於以下cars.json,我希望使用D3來扁平化層次結構,爲每個model的每個型號year提供一個新行。因此,總共應該有9個訂單項,每個品牌和型號都有3個訂單項。

我確定我正在接近這個錯誤,但我在D3有點新,我不知道該怎麼想。我已經使用d3.nest看過其他問題,但由於我沒有嘗試對任何內容進行分組,因此似乎不適用。謝謝!

cars.json

[ 
    { 
    "make": "Ford", 
    "model": "Escape", 
    "years": [ 
     { 
     "year": 2013, 
     "price": 16525 
     }, 
     { 
     "year": 2014 
     }, 
     { 
     "year": 2015 
     } 
    ] 
    }, 
    { 
    "make": "Kia", 
    "model": "Sportage", 
    "years": [ 
     { 
     "year": 2012 
     }, 
     { 
     "year": 2013, 
     "price": 16225 
     }, 
     { 
     "year": 2014 
     } 
    ] 
    }, 
    { 
    "make": "Honda", 
    "model": "CR-V", 
    "years": [ 
     { 
     "year": 2008 
     }, 
     { 
     "year": 2009 
     }, 
     { 
     "year": 2010, 
     "price": 12875 
     } 
    ] 
    } 
] 

期望的輸出

<table> 
    <thead> 
     <tr><th>Make</th><th>Model</th><th>Year</th><th>Price</th></tr> 
    </thead> 
    <tbody> 
     <tr><td>Ford</td><td>Escape</td><td>2013</td><td>16525</td></tr> 
     <tr><td>Ford</td><td>Escape</td><td>2014</td><td></td></tr> 
     <tr><td>Ford</td><td>Escape</td><td>2015</td><td></td></tr> 
     <tr><td>Kia</td><td>Sportage</td><td>2012</td><td></td></tr> 
     <tr><td>Kia</td><td>Sportage</td><td>2013</td><td>16225</td></tr> 
     <tr><td>Kia</td><td>Sportage</td><td>2014</td><td></td></tr> 
     <tr><td>Honda</td><td>CR-V</td><td>2008</td><td></td></tr> 
     <tr><td>Honda</td><td>CR-V</td><td>2009</td><td></td></tr> 
     <tr><td>Honda</td><td>CR-V</td><td>2010</td><td>12875</td></tr> 
    </tbody> 
</table> 

當前的嘗試

<table id="cars_table"> 
    <thead> 
     <th>Make</th><th>Model</th><th>Year</th><th>Price</th> 
    </thead> 
    <tbody></tbody> 
    <tfoot></tfoot> 
</table> 

<script> 
(function(){ 
    d3.json('/static/cars.json', function(error, cars) { 

     var tbody = d3.select('tbody') 
     rows = tbody.selectAll('tr').data(cars).enter().append('tr') 

     rows.append('td').html(function(d) { 
      return d.make 
     }) 

     rows.append('td').html(function(d) { 
      return d.model 
     }) 

     var years = rows.append('td').html(function(d) { 
      return d.years 
      // don't want this nested; probably should be peeled out into another `selectAll`, but I don't know where? 
     }) 
    }) 
})() 
</script> 

回答

2

必須在渲染數據之前將數據平坦化,以便每行有一個數據(並且因爲行不嵌套,所以數據不應嵌套)。這樣你顯示的表格渲染代碼就可以工作。

理想情況下,您已經將數據平面化。 CSV非常適合傳輸平面數據,這通常是關係數據庫的結果。在你的情況下,列將是「製造」,「型號」,「年份」和「價格」,其中每個品牌/型號出現3次 - 每年一次。

如果你不能修改數據,那麼一旦它加載,就把它壓扁成JS。我幾乎肯定沒有這一個D3實用程序(d3.nest()做你問做什麼的對面),但它足夠簡單用一個循環做到這一點:

var flatCars = [] 
cars.forEach(function(car) { 
    car.years.forEach(function(carYear) { 
    flatCars.push({ 
     make: car.make, 
     model: car.model, 
     year: carYear.year, 
     price: carYear.price 
    }); 
    }); 
}); 

var flatCars = cars.reduce(memo, car) { 
    return memo.concat(
    car.years.map(function(carYear) { 
     return { 
     make: car.make, 
     model: car.model, 
     year: carYear.year, 
     price: carYear.price 
     } 
    }); 
); 
}, []) 
+0

完美的感覺;非常感謝。如果你有第二個,你可以解釋'reduce',爲什麼你可以在'forEach'上使用它? – allanberry

+1

我包含'.reduce()'示例主要是爲了幻想:)通過查看使用它的代碼很難收集它的工作方式,但是如果閱讀[它的工作原理說明],應該變得足夠清晰https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)。 – meetamit

+1

在這種情況下,優點是微不足道的,但仍然:減少內部函數不需要引用在這些函數之外定義的變量(第一個示例中爲'flatCars.push')。這意味着你可以寫'flatCars = cars.reduce(flattenCar)',其中'flattenCar'在別處定義。這可能會提高性能(這裏可以忽略不計)。它也實現了1而不是2行的結果,這可以使得代碼更具可讀性。即開發人員可能會因'var flatCars = []'的作用而感到困惑 - 直到他們讀取下一代碼塊。 – meetamit

2

您必須在將數據傳遞給D3的data()方法之前將其平坦化。 D3應該只負責將數據結構轉換爲DOM樹。換句話說:如果您希望嵌套DOM結構,請使用嵌套數據結構。

所以,壓平像這樣的數據(使用lodash這裏):

data = _.flatten(data.map(function (model) { 
    return model.years.map(function (year) { 
     return _.assign(year, _.pick(model, 'make', 'model')); 
    }); 
})); 

,然後將其傳遞給data()方法。在這裏工作代碼:http://codepen.io/anon/pen/grPzPJ?editors=1111