忙しくする毎日

実質¥38,000の義足が新調された。なかなか足に合わない。
それなのに、テントの倉庫の屋根に上り破れたテントを補修する。


集成材のカットと購入してから1カ月、運送会社のトラブルにより香川県と3往復した模様。
やっと届いた材料で仕事で使う机を2つ製作する


夜は、勉強する

<!DOCTYPE html>
<meta charset="utf-8">
<title>テCSVファイルに関連したもの</title>

<style>
  section, li { width:60em; line-height :150%;}
  section {
    padding-bottom:3em;
    border-bottom: 2px #4d4 dotted;
  }
  #SAMPLE_TABLE thead {
    background: #080;
    color:#ff0;
  }
  #SAMPLE_TABLE tbody tr {
    background:#f8f8f8;
  }
  #SAMPLE_TABLE tbody tr:nth-child(even){
    background:#eef;
  }
  #SAMPLE_TABLE tbody td:nth-child(1){
    background:#3a3;
    color: #FFF;
    text-align: center;
  }
  #SAMPLE_TABLE td {
    padding : 0 .3em;
  } 
  code {
    white-space: pre-wrap;
  }
</style>

<body>
<h1>CSVファイルに関連したものをライブラリ化できる?</h1>
<ol>
  <li>
    <a href="#SECTION-1">CSVファイルの一般的書式</a>
  <li>
    <a href="#SECTION-2">CSVファイルのサンプル</a>
  <li>
    <a href="#SECTION-3">ライブライ化で、目標とするもの</a>
</ol>

<section id="SECTION-1">
  <h2>CSVファイルの一般的書式</h2>
  <p>
    <a href="http://www.ietf.org/rfc/rfc4180.txt">原文</a>は、英語なので
    <a href="http://www.kasai.fm/wiki/rfc4180jp">邦訳</a>
    (<a href="http://www.kasai.fm/">アルプス登山の玄関口笠井家</a>)を参照の事。
  <p>
    MIMEタイプ は、弄らないので略して引用する。
  <ul>
    <li>
      各レコードは、改行(CRLF)を区切りとする、分割された行に配置される。
    <li>
      ファイル末尾のレコードの終端には、改行はあってもなくてもよい。
    <li>
      ファイルの先頭には、オプションとして、通常行と同一の書式を持つ、
      ヘッダ行が存在してもよい。このヘッダは、ファイル中の各フィールドの名称を保持し、
      ファイルの残りの部分にある各レコードが持っているのと、同じ数のフィールドを
      持つべきである。
    <li>
      ヘッダと各レコードは、コンマで区切られた、一つ以上のフィールドを含む。
      各行が保持するフィールドの数は、ファイル全体を通じ、同一であるべきである。
      スペースは、フィールドの一部とみなす。無視すべきではない。
      最後のフィールドは、コンマで終わってはならない。
    <li>
      (レコード中の) 各フィールドは、それぞれダブルクォーテーションで囲んでも
      囲わなくてもよい。
      フィールドがダブルクォーテーションで囲まれていない場合、そのフィールドの値には、
      ダブルクォーテーションが含まれてはいけない。
    <li>
      改行(CRLF)、ダブルクォーテーション、カンマを含むフィールドは、ダブルクォーテーションで
      囲むべきである。
    <li>
      フィールドがダブルクォーテーションで囲まれている場合、フィールドの値に含まれる
      ダブルクォーテーションは、その直前にひとつダブルクォーテーションを付加して、
      エスケープしなければならない。
  </ol>
</section>

<section id="SECTION-2">
  <h2>サンプルとするCSV文字列</h2>
  <div>
<textarea cols="60" rows="10" id="SAMPLE_CSV">
ID,氏名,シメイ,性別,電話番号,生年月日,年齢,血液型
1,椎名和代,シイナカズヨ,女,0312581924,1959/08/27,51,O
2,村井博文,ムライヒロフミ,男,0382192989,1985/01/14,26,AB
3,西川俊子,ニシカワトシコ,女,0343460909,1971/10/03,39,B
4,黒田睦美,クロダムツミ,女,0315211328,1977/11/11,33,O
5,五十嵐颯,イガラシソウ,男,0335494621,1974/02/11,37,O
6,小池真理子,コイケマリコ,女,0320317992,1969/11/10,41,AB
7,久保宏美,クボヒロミ,女,0330062953,1955/11/05,55,O
8,岩永音葉,イワナガオトハ,女,0348956804,1958/02/07,53,A
9,庄司貞次,ショウジテイジ,男,0355093904,1962/10/19,48,A
10,赤塚綾菜,アカツカアヤナ,女,0399960433,1954/05/07,57,A
11,杉野茂,スギノシゲル,男,0302024645,1964/11/22,46,A
12,北原紗耶,キタハラサヤ,女,0380462601,1974/05/11,37,AB
13,福永孝二,フクナガコウジ,男,0333378739,1972/05/07,39,O
14,増井武彦,マスイタケヒコ,男,0382039671,1964/08/22,46,A
15,新谷悦代,アラヤエツヨ,女,0377460694,1987/08/06,23,A
16,小野寺忠三,オノデラチュウゾウ,男,0305429745,1951/08/11,59,B
17,富山葉菜,トミヤマハナ,女,0312582616,1982/12/27,28,O
18,森谷一寿,モリヤカズヒサ,男,0348789450,1979/12/18,31,A
19,妹尾空,セノオソラ,女,0322594281,1962/07/03,48,O
20,塚本重樹,ツカモトシゲキ,男,0325976517,1961/11/26,49,A
21,島友子,シマトモコ,女,0327032866,1962/09/07,48,A
22,河上照夫,カワカミテルオ,男,0378647656,1968/05/31,43,A
23,玉木香織,タマギカオリ,女,0319236941,1984/09/18,26,O
24,小倉孝之,オグラタカユキ,男,0328378613,1990/06/27,21,O
25,狩野重雄,カノウシゲオ,男,0305641486,1958/01/18,53,O
26,坂田可憐,サカタカレン,女,0338182032,1986/06/10,25,O
27,海老原正男,エビハラマサオ,男,0330037767,1963/06/29,47,B
28,富山彩花,トミヤマアヤカ,女,0345313806,1964/05/29,47,A
29,石田義和,イシダヨシカズ,男,0352023930,1961/04/25,50,AB
30,小野寺聖子,オノデラセイコ,女,0355404261,1972/06/04,39,O
</textarea>
  </div>
  <p>
    もちろん<a href="http://domes.lingua.heliohost.org/libxml-js/src-test/jsom/tdc.html">ここ</a>からコピペ
</section>

<section id="SECTION-3">
  <h2>ライブライ化で、目標とするもの</h2>
  <pre>
C$V(table)
・table が Array ならそのまま返す。
・table が String(CSV ソース)なら Array 化して返す。
・table が HTMLTableElement なら Array 化して返す。

C$V(table, query)
・上記に加え、query を適用した Array を返す。
・query は { select:..., where:..., order:... } のようなメンバを持つ Object// 例:男性か、二十歳以下の女性のレコードを、男女順および年齢の高い順に並べ替えた上で、
名前と給料の列を取得し、給料の総額を出す。

C$V(table, {
  select: '名前, 給料',
  where: '性別 = 男性 | (性別 <= 20 & 性別 = 女性)',
  order: '+性別; -年齢'
}).reduce(function (sum, row) {
  return sum + row[1];
});  
  </pre>
</section>

<sction id="SECTION-4">
  <h2>操作されるテーブル</h2>
  <table border="1" id="SAMPLE_TABLE">
    <tr>
      <th>a <td>b <td>c
    <tr>
      <th>d <td>e <td>f
  </table>
</section>

<section id="SECTION-5">
  <h2>JavaScriptのコード</h2>
  <code id="CODE" class="JavaScript">
  </code>
</section>


<script>
(function () {
  // csvを解析するための関数群
  
  function trim (str) {//先端と終端の改行を取り除く
    return String (str).replace (/^[\r\n]+|[\r\n]+$/g, '');
  }
  
  //以下の関数 isStrict、isLoose は、CSVを厳格にするための関数
  //(「英語圏の文字だけを利用するか?」の意の厳格ではない。)
  //条件によりに「判断する関数」を代入し、それらに任せている。
  //(「関数は呼び出しコストが高い」との意で、自分的に頻繁に使わない技法。
  //   読みやすさと生産性とならこの書き方→見習う

  //各行のフィールドの数が同じでない場合、それを弾く。
  function isStrict (flength, findex) {
    return flength === findex;
  }
  
  //引数も渡されるが無視
  function isLoose () {
    return true;
  }

  //CSV文字列を配列に
  function csvToAry (stringData, strict, separator) {
    //初期値の代入と区切り文字の判別を行う→見習う
    if ((separator || (separator = ',')) === ',' || separator === '\t') {
      var isWellFormed = strict ? isStrict : isLoose;
        
      var pattern = new RegExp ('^(?:' + separator +    // ,で始まるか?
                                '|(?:\\r\\n|\\r|\\n)' + // 改行文字か?
                                '|"(?:""|[^"])*"' +
                                '|[^\\r\\n"' + separator + '][^\\r\\n' + separator + ']*' +
                                ')');
      var flength = 0; //フィールドの数
      var findex  = 0; //現行のフィールド数
      var fields  = [ '' ];
      var records = [ fields ]; //戻り値
      var token; //切り出した文字
      
      for (
        //前後の改行を取り除く
        stringData = trim (stringData); 
        //トークンの判定(配列成功)と変数の上書き→見習う
        (token = pattern.exec (stringData)) && (token = token[0]);
        //トークンの次の文字を対象とする
        stringData = stringData.slice (token.length)
      ) {
        switch (token.charAt (0)) {//切り出されたトークンの先頭の文字で今後判断する
            
        case separator :
          fields[++findex] = ''; // セパレータが現れる度、次のフィールドを初期化
          break;
            
        case '\r' ://「\r\n」は、正規表現で2文字で切り出しているが最初の1文字判定でOK!
        case '\n' :
          if (! isWellFormed (flength || (flength = findex), findex))
            throw new Error ('malformed CSV.');

          fields = records[records.length] = [''];//改行なので次を初期化
          findex = 0;
          break;
            
        case '"' :
          //「”」で始まると「”」で終わるはずなので中抜きする、
          //さらに連続した「""」は、「"」とする
          fields[findex] = token.slice (1, -1).replace (/""/g, '"');
          break;
            
        default :
          fields[findex] = token;
          break;
        }
      }
      
      //切り出す文字が無い、(各行のフィールド数がすべて同じなら)
      if (stringData === '' && isWellFormed (flength, findex)) {
        return records;
      }
    }
    
    throw new Error ('malformed CSV.');
  }
  
  //__________

  // HTMLTableElementを配列化する関数群
  function getTextByCell (cell) {//セルのテキスト取得
    return cell.textContent.trim ();//空白を無視する?
  }
  
  function getCells (row) {// 行のセルを取得
    return [].map.call (row.cells, getTextByCell);
  }
  
  function tableToAry (table) {// trを走査
    return [].map.call (table.rows, getCells);
  }
  
  //__________
  
  //本体
  function C$V (table, query) {
    if (1 > arguments.length)
      return null;

    var ary; // [ ];
    
    switch (true) {
    case Array.isArray (table) :
      ary = table;
      break;

    case 'string' === typeof (table):
      ary = csvToAry (table);
      break;
    
    case table instanceof HTMLTableElement:
      ary = tableToAry (table);
      break;
    
    default :
      throw new Error ('無効な引数')
    }
    return ary;
  }
  
  //____
  
  this.C$V = C$V;
}) ();



// 配列をテーブルにする
(function () {

  function insertTD (text) {
    this.insertCell (-1).textContent = text;
  }
  
  function insertTR (record) {
    record.forEach (insertTD, this.insertRow (-1));
  }
  
  function setTableSectionElement (ary) {
    ary.forEach (insertTR, this);
  }

  function ary2Table (ary, table, allTbody) {
    if (2 > arguments.length)
      throw new Error ();
    if (! Array.isArray (ary))
      throw new Error ('Not Array');
    if (! table instanceof HTMLTableElement)
      throw new Error ('Not HTMLTableElement');
    
    while (table.hasChildNodes ())
      table.removeChild (table.firstChild);
    
    if (! allTbody)
      setTableSectionElement.call (table.createTHead (), ary.splice (0,1));

    setTableSectionElement.call (table.createTBody (), ary);
  }
  
  this.ary2Table = ary2Table;
}) ();

//______________________
//もともとあるテーブルから配列に
alert(C$V (document.getElementById ('SAMPLE_TABLE')).join("\n"));

//CSV文字列を配列にしてテーブルに
ary2Table (
  C$V (document.getElementById ('SAMPLE_CSV').value),
  document.getElementById ('SAMPLE_TABLE')
);
</script>
<script>
document.getElementById ('CODE').textContent = document.querySelector ('script').textContent;

</script>