pugで少し複雑なtableを組んでみる

ちょうど下表のような感じでrowspanの混じったtableを作る機会があって、いつか使い回すときがくるだろうと思って、Iterationでいい感じにできないか試してみました
![表組みサンプル](https://stat.ameba.jp/user_images/20190121/17/mastuyama-rikujou-kids2/a7/69/j/o0600044914342804352.jpg)
## 考え方
ひとまず10行ある中で、小学生に4行(rowspan:4)・中学生に2行(rowspan:2)・一般に4行(rowspan:4)のデータを持つ必要があるので、変数としては以下の様な構成になると思います

```javascript:variables.pug
-
  const content = [
    {
       category: '小学生',
       data: [
         {
           age: '3年生以下',
           sex: '男女',
           distance: '1km',
           start: '10時00分'
         },
         {
           age: '4年',
           sex: '男女',
           distance: '2km',
           start: '10時15分'
         },
         {
           age: '5年',
           sex: '男女',
           distance: '2km',
           start: '10時30分'
         },
         {
           age: '6年',
           sex: '男女',
           distance: '2km',
           start: '10時45分'
         }
       ]
    },
    {
       category: '中学生',
       data: [
         {
           age: '1〜3年',
           sex: '男子',
           distance: '3km',
           start: '11時15分'
         },
         {
           age: '1〜3年',
           sex: '女子',
           distance: '3km',
           start: '11時00分'
         }
       ]
    },
    {
       category: '一般',
       data: [
         {
           age: '制限なし',
           sex: '女子',
           distance: '3km',
           start: '11時00分'
         },
         {
           age: '29才以下',
           sex: '男子',
           distance: '5km',
           start: '11時30分'
         },
         {
           age: '30才〜49才以下',
           sex: '男子',
           distance: '5km',
           start: '11時30分'
         },
         {
           age: '50才以上',
           sex: '男子',
           distance: '5km',
           start: '11時30分'
         }
       ]
    }
  ]

```
※ちなみにNOはindex値から取ればいいので省略

とりあえず10行作らないといけないので、Objectをループさせるだけじゃダメ
dataの数だけtrが生成されるようにします

```javascript:index.pug
tbody
  each val, index in content
    each data, dataIndex in val.data
      tr
```

現状の出力結果

```html:index.html
<tbody>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
  <tr></tr>
</tbody>

```
理想としては以下の様に1周目だけにNOと部門のtdが生成され、ついでにrowspan属性を付与してほしいわけです

```html:index.html
<tr>
  <td rowspan="4">1</td>
  <td rowspan="4">小学生</td>
  <td>3年生以下</td>
  <td>男女</td>
  <td>1km</td>
  <td>10時00分</td>
</tr>
<tr>
  <td>4年</td>
  <td>男女</td>
  <td>2km</td>
  <td>10時15分</td>
</tr>
<tr>
  <td>5年</td>
  <td>男女</td>
  <td>2km</td>
  <td>10時30分</td>
</tr>
<tr>
  <td>6年</td>
  <td>男女</td>
  <td>2km</td>
  <td>10時45分</td>
</tr>
```
つまりdataIndexが0のときに、NOと部門のtdを生成する処理をしてあげればよいということになります

```javascript:index.pug
tbody
  each val, index in content
    each data, dataIndex in val.data
      tr
        // 1周目のときのみNOと部門のtdを生成
        if dataIndex === 0
          // NOの表示
          // rowspanはdataの数を付与
          td(rowspan=val.data.length)
            = index + 1 // indexは0スタートなので+1してあげる

          // 部門の表示
          td(rowspan=val.data.length)
            | #{val.category}

        // あとは毎回の処理
        td= data.age
        td= data.sex
        td= data.distance
        td= data.start

```
## 結果

```html:index.html
<tbody>
  <tr>
    <td rowspan="4">1</td>
    <td rowspan="4">小学生</td>
    <td>3年生以下</td>
    <td>男女</td>
    <td>1km</td>
    <td>10時00分</td>
  </tr>
  <tr>
    <td>4年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時15分</td>
  </tr>
  <tr>
    <td>5年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時30分</td>
  </tr>
  <tr>
    <td>6年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時45分</td>
  </tr>
  <tr>
    <td rowspan="2">2</td>
    <td rowspan="2">中学生</td>
    <td>1〜3年</td>
    <td>男子</td>
    <td>3km</td>
    <td>11時15分</td>
  </tr>
  <tr>
    <td>1〜3年</td>
    <td>女子</td>
    <td>3km</td>
    <td>11時00分</td>
  </tr>
  <tr>
    <td rowspan="4">3</td>
    <td rowspan="4">一般</td>
    <td>制限なし</td>
    <td>女子</td>
    <td>3km</td>
    <td>11時00分</td>
  </tr>
  <tr>
    <td>29才以下</td>
    <td>男子</td>
    <td>5km</td>
    <td>11時30分</td>
  </tr>
  <tr>
    <td>30才〜49才以下</td>
    <td>男子</td>
    <td>5km</td>
    <td>11時30分</td>
  </tr>
  <tr>
    <td>50才以上</td>
    <td>男子</td>
    <td>5km</td>
    <td>11時30分</td>
  </tr>
</tbody>
```
index.pugの中身はすっきりして、あとはvariables.pugを編集するだけで済みますね

## これだと最後の11時30分がrowspanされていない？
そんなイレギュラーなものはこんな感じで処理してしまいましょう
上記までの内容が理解できていればなんてことないです

```javascript:variables.pug
-
  const content = [
    {
      category: '小学生',
      data: [
        {
          age: '3年生以下',
          sex: '男女',
          distance: '1km',
          start: '10時00分'
        },
        {
          age: '4年',
          sex: '男女',
          distance: '2km',
          start: '10時15分'
        },
        {
          age: '5年',
          sex: '男女',
          distance: '2km',
          start: '10時30分'
        },
        {
          age: '6年',
          sex: '男女',
          distance: '2km',
          start: '10時45分'
        }
      ]
    },
    {
      category: '中学生',
      data: [
        {
          age: '1〜3年',
          sex: '男子',
          distance: '3km',
          start: '11時15分'
        },
        {
          age: '1〜3年',
          sex: '女子',
          distance: '3km',
          start: '11時10分'
        }
      ]
    },
    {
      category: '一般',
      data: [
        {
          age: '制限なし',
          sex: '女子',
          distance: '3km',
          start: '11時00分'
        },
        {
          age: '29才以下',
          sex: '男子',
          distance: '5km',
          start: '11時30分',
          colspan: 3
        },
        {
          age: '30才〜49才以下',
          sex: '男子',
          distance: '5km',
        },
        {
          age: '50才以上',
          sex: '男子',
          distance: '5km',
        }
      ]
    }
  ]
```

```javascript:index.pug
tbody
  each val, index in content
    each data, dataIndex in val.data
      tr
        if dataIndex === 0
          td(rowspan=val.data.length)
            = index
          td(rowspan=val.data.length)
            | #{val.category}
        td= data.age
        td= data.sex
        td= data.distance
        if data.start
          td(rowspan=data.colspan)= data.start
```

```html:index.html
<tbody>
  <tr>
    <td rowspan="4">0
    </td>
    <td rowspan="4">小学生</td>
    <td>3年生以下</td>
    <td>男女</td>
    <td>1km</td>
    <td>10時00分</td>
  </tr>
  <tr>
    <td>4年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時15分</td>
  </tr>
  <tr>
    <td>5年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時30分</td>
  </tr>
  <tr>
    <td>6年</td>
    <td>男女</td>
    <td>2km</td>
    <td>10時45分</td>
  </tr>
  <tr>
    <td rowspan="2">1
    </td>
    <td rowspan="2">中学生</td>
    <td>1〜3年</td>
    <td>男子</td>
    <td>3km</td>
    <td>11時15分</td>
  </tr>
  <tr>
    <td>1〜3年</td>
    <td>女子</td>
    <td>3km</td>
    <td>11時10分</td>
  </tr>
  <tr>
    <td rowspan="4">2
    </td>
    <td rowspan="4">一般</td>
    <td>制限なし</td>
    <td>女子</td>
    <td>3km</td>
    <td>11時00分</td>
  </tr>
  <tr>
    <td>29才以下</td>
    <td>男子</td>
    <td>5km</td>
    <td rowspan="3">11時30分</td>
  </tr>
  <tr>
    <td>30才〜49才以下</td>
    <td>男子</td>
    <td>5km</td>
  </tr>
  <tr>
    <td>50才以上</td>
    <td>男子</td>
    <td>5km</td>
  </tr>
</tbody>
```