Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

studistory | イテレーション3 | 各項目にソート機能を追加する

概要

各項目にソート機能を追加する

内容

各項目にソート機能を追加します。
Array#sort(comparetor)を利用しますが、ソート項目ごとに関数を書くのが嫌なので
evalを使ったメタプログラミングにして、DRYな実装にしてみました。
(comparator.js部)

comparator.js

var method_keys = ["TodoDateAsc", "TodoDateDesc", "SubjectAsc", "SubjectDesc",
                  "SummaryAsc", "SummaryDesc", "EstimateAsc", "EstimateDesc",
                  "StartAsc" , "StartDesc", "EndAsc", "EndDesc", "TotalAsc" , "TotalDesc"];
var attribute_keys = ["todo_date", "todo_date", "subject", "subject",
                    "summary", "summary", "estimate", "estimate",
                    "start", "start", "end", "end", "total" , "total"];
var large_ret_values = [1 , -1, 1, -1,
                      1 , -1, 1, -1,
                      1 , -1, 1, -1, 1 , -1,];
var less_ret_values = [-1 , 1, -1, 1,
                      -1 , 1, -1, 1,
                      -1 , 1, -1, 1, -1 , 1];

for (var i=0;i<method_keys.length;i++) {
  var func = (function () {/*
function compareBy#{MethodKey}(one, other) {
if (one.#{AttributeKey} < other.#{AttributeKey})
  return #{LessValue};
if (one.#{AttributeKey} > other.#{AttributeKey})
  return #{LargeValue};
return 0;
}
  */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];

  func = func.replace("#{MethodKey}", method_keys[i]);
  func = func.replace(/#{AttributeKey}/g, attribute_keys[i]);
  func = func.replace(/#{LargeValue}/g, large_ret_values[i]);
  func = func.replace(/#{LessValue}/g, less_ret_values[i]);
  eval(func);
}

studistory.html

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>studistory</title>
  <link rel="stylesheet" type="text/css" href="studistory.css" />
  <script type="text/javascript" src="201308.js"></script>
  <script type="text/javascript" src="201309.js"></script>
  <script type="text/javascript" src="comparator.js"></script>
  <script type="text/javascript">
  function search(sortKey, ascDesc) {
    ascDesc = ascDesc || "Asc";
    enableMonths = ["201308", "201309"];
    var tbody = document.getElementsByTagName("tbody")[0];
    crearList(tbody);
    searchMonth = document.getElementById("month").value.replace("-", "");
    if (!isValidMonths(searchMonth, enableMonths)) return;
    list = getList(searchMonth, sortKey, ascDesc);
    outputList(tbody, list);
    setMessage(searchMonth.slice(0,4) + "年" + searchMonth.slice(4,6) + "月" + "の検索実行が完了しました")
  }

  function crearList(tbody) {
    tbody.innerHTML = '';
  }

  function isValidMonths(searchMonth, enableMonths) {
    if (enableMonths.indexOf(searchMonth) == -1) {
      setMessage("その日付の学習履歴は存在しません")
      return false;
    }
    return true;
  }

  function setMessage(message) {
    document.getElementById("message").innerText = message;
  }

  function getList(searchMonth, sortKey, ascDesc) {
    // ヒアドキュメントもどき
    const TEMPLATE = (function () {/*
    <tr id="#{id}">
      <td id="tododate_#{id}">#{todo_date}</td>
      <td id="subject_#{id}">#{subject}</td>
      <td id="summary_#{id}">#{summary}</td>
      <td id="estimate_#{id}">#{estimate}</td>
      <td id="start_#{id}">#{start}</td>
      <td id="end_#{id}">#{end}</td>
      <td id="total_#{id}">#{total}</td>
    </tr>
    */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];

    var monthJsons = null;
    eval("monthJsons = eval(todos_" + searchMonth + ");");

    comparator = eval("compareBy" + sortKey + ascDesc);
    monthJsons.sort(comparator);

    line_htmls = [];
    for (var i = 0;i < monthJsons.length; i++) {
      line = TEMPLATE;
      line = line.replace(/#{id}/g, monthJsons[i].id);
      line = line.replace("#{todo_date}", monthJsons[i].todo_date);
      line = line.replace("#{subject}", monthJsons[i].subject);
      line = line.replace("#{summary}", monthJsons[i].summary);
      line = line.replace("#{estimate}", monthJsons[i].estimate);
      line = line.replace("#{start}", monthJsons[i].start);
      line = line.replace("#{end}", monthJsons[i].end);
      line = line.replace("#{total}", monthJsons[i].total);
      line_htmls.push(line);
    }
    return line_htmls.join("\n");
  }

  function outputList(tbody, list) {
    tbody.innerHTML = list;
  }
  </script>
</head>
<body>
  <header>
    <h1 id="title">studistory</h1>
    <p id="summary">学習履歴管理を行います</p>
    <p id="message" style="color:red;">検索を実行してください</p>
  </header>
  <section id="search">
    <form name="studistoryform" id="studistoryform">
      <p><input type="month" name="month" id="month" /></p>
      <p><input type="button" value="search" onclick="search('Start')"/></p>
    </form>
  </section>
  <section id="list">
    <table id="list" border="1">
      <thead>
        <tr>
          <th>todo date<span onclick="search('TodoDate')"></span> <span onclick="search('TodoDate', 'Desc')"></span></th>
          <th>subject<span onclick="search('Subject')"></span> <span onclick="search('Subject', 'Desc')"></span></th>
          <th>summary<span onclick="search('Summary')"></span> <span onclick="search('Summary', 'Desc')"></span></th>
          <th>estimate<span onclick="search('Estimate')"></span> <span onclick="search('Estimate', 'Desc')"></span></th>
          <th>start<span onclick="search('Start')"></span> <span onclick="search('Start', 'Desc')"></span></th>
          <th>end<span onclick="search('End')"></span> <span onclick="search('End', 'Desc')"></span></th>
          <th>total<span onclick="search('Total')"></span> <span onclick="search('Total', 'Desc')"></span></th>
        </tr>
        <tbody>
        </tbody>
      </thead>
    </table>
  </section>
</body>
</html>

画面

初期検索

初期検索結果=>totalの昇順ソート

初期検索結果=>totalの降順ソート