D3.jsでサクッとFizzBuzzを可視化する
D3.jsを使って、FizzBuzzの結果をテーブルで表示します。
DOMにデータを束縛するD3の機能を使うと非常に簡単に作成することができます。
サンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
(function(){ var START = 1,END = 100; var FizzBuzz = function(num){ if(num%15==0) return 'FizzBuzz'; if(num%5==0) return 'Buzz'; if(num%3==0) return 'Fizz'; return num; } //fizz buzz リスト作成 var list = d3.range(START,END+1).map(FizzBuzz); //リストを10個づつの配列に分割 var fbDataSet = []; for(var i=0; i < END; i+=10){ fbDataSet.push( list.slice(i, i+10) ); } //table要素作成 var table = d3.select('body').append('table'); //tr要素作成 var tr = table.selectAll('tr') .data(fbDataSet) .enter() .append('tr'); //td要素作成 var td = tr.selectAll('td') .data(function(d){ return d }) //今回のポイント .enter() .append('td') .attr({ class:function(d){ return (typeof d=='string') ? d : null; } }) .text(function(d){ return d } ); })(); |
解説
ちょっとだけ解説
FizzBuzz配列を作る
1 2 3 4 5 6 7 8 9 10 |
var START = 1,END = 100; var FizzBuzz = function(num){ if(num%15==0) return 'FizzBuzz'; if(num%5==0) return 'Buzz'; if(num%3==0) return 'Fizz'; return num; } var list = d3.range(START,END+1).map(FizzBuzz); |
d3.rangeは等差数列を含むリストを生成します。d3.range(1, 100)を実行すると以下の配列が生成されます。
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …… 99]
2番目の引数は要素数の指定なので、1〜100までの数列を作りたいときは+1します。
生成した配列にmapメソッドを用いてFizzBuzz関数を適用します。実行結果が以下のようになります。
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, …… "Buzz"]
配列を分割してデータセットを作る
1 2 3 4 |
var fbDataSet = []; for(var i=0; i < END; i+=10){ fbDataSet.push( list.slice(i, i+10) ); } |
作成したFizzBuzz配列を10個づつに分割して2次元配列のデータセットを作成します。
[
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz"],
[11, "Fizz", 13, 14, "FizzBuzz", 16, 17, "Fizz", 19, "Buzz"],
(中略)
[91, 92, "Fizz", 94, "Buzz", "Fizz", 97, 98, "Fizz", "Buzz"]
]
テーブルを作成する
body要素の中にtable要素を追加します。
1 |
var table = d3.select('body').append('table'); |
データセットを元にtr要素を追加します。
1 2 3 4 |
var tr = table.selectAll('tr') .data(fbDataSet) .enter() .append('tr'); |
この段階でブラウザの開発者ツールを使いDOMの状態を覗いてみると、以下のようにTR要素が追加されているのがわかります。
戻り値を収めている変数trには、追加した要素のオブジェクトが入っています。
d3.select(‘tr’)で中身を覗いていると__data__プロパティにデータセットが束縛されているのがみてとれます。
tdを追加する
今回のポイント、td要素を追加します。
1 2 3 4 5 6 7 8 9 10 |
var td = tr.selectAll('td') .data(function(d){ return d }) .enter() .append('td') .attr({ class:function(d){ return (typeof d=='string') ? d : null; } }) .text(function(d){ return d } ); |
D3になれていないと混乱するのが以下の部分。
tr.selectAll('td')
.data(function(d){ return d })
.enter()
.append('td')
dataメソッドにコールバックを設定し、引数をreturnでそのまま返しています。
これが何をやっているのかというと、親要素(tr)に束縛されたデータセットがコールバックの引数として渡されるので、それをそのまま戻り値としてdataメソッドに渡すことでtd要素を各tr要素の配下に追加しています。
引数に何が渡されているかを見るために、returnの前にconsole.logを追加して中身を見てみるとわかりやすいです。
tr.selectAll('td').data(function(d){ console.log(d); return d })
> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz"]
> [11, "Fizz", 13, 14, "FizzBuzz", 16, 17, "Fizz", 19, "Buzz"]
> ["Fizz", 22, 23, "Fizz", "Buzz", 26, "Fizz", 28, 29, "FizzBuzz"]
(中略)
> [91, 92, "Fizz", 94, "Buzz", "Fizz", 97, 98, "Fizz", "Buzz"]
この段階でDOMの状態を覗いてみるとtd要素が追加されているのが分かります。
残りの構文(attrとtext)では、tdに渡されたデータが文字列(“Fizz”や”Buzz”、”FizzBuzz”)だった場合、tdのcalss属性として設定し、テキスト要素を追加しています。
最終的な出力は以下のようになります。
「dataメソッドのコールバックの引数に、要素に束縛されているデータセットが渡される」というD3の機能を使うと、メンドウな処理も結構簡単に作成することができます。