JavaScript
正規表現
CSV
jsnote

JavaScriptでCSV形式の文字列を正規表現でパースして2次元配列に入れる方法

背景

JavaScriptでCSVをパースしてグラフを描いてみたいと思った.ライブラリを使おうかとも思ったけれど, 正規表現でパースしてみたかったので、正規表現の練習も兼ねてやってみた.

前提

FileReaderを使ってCSVファイルを読み込んで, 文字列としてテキストの中身が取得されているとする.

FileReaderのMDNのページはこちら

CSV

少し雑なCSV形式の文字列が読み込まれたと想定する. エクセルで作ったデータをcsvで吐き出して、読み込むとこんな感じになっている.

const sample =
  " 'title', 'tmp1', 'tmp2'\r\n \
          1,      1,     1 \r\n \
                           \r\n \
           ,       ,       \r\n \
          3,      4,     5 \r\n \
          5,      5,     5 \r\n \
          2,      3,     4 \r\n";

正規表現

split, map, filterとか使って, csvをパースするための関数はこんな風になった.

step1: 改行コード(\n, \r, \n\r)でスプリットして, 配列に入れる.
step2: 空白だけの行, タブだけの行, カンマだけの行を取り除く.
step3: 各行で先頭と末尾の空白を取り除く.
step4: 各行でカンマの後に続く空白を取り除く.
step5: 各行でカンマでスプリットして配列に入れる.
step6: 各行でスプリットして配列に入れた値が数値の場合は, 文字から数値に変換する.

これで2次元配列で読み込める.

const csvParse = csv => csv
  .split(/\r\n|\n|\r/) //split by line feed codes
  .filter((k)=>k.match(/[^,\s]/)) //remove empty lines
  .map((k)=>k.trim() //remove white spaces of begining and end of line
    .replace(/,\s+/g,",") //remove white spaces
    .split(",") //split by cannma
    .map((l)=>isNaN(l)? l:parseFloat(l)) //convert string to flot
  ); 

転置

csvParseでパースしたままだと, 各行ごとに配列に収められているのでグラフを描く時に不便. データは列毎に並べて,一番上の行はインデックスになっていることが多い.
そこで, 転置しないといけない. for文でpushする方が速いと思うけれど, 一行で書けるmapをが好き. 大した量のデータでなければ, 転置はこれで十分いける.

 const transpose = A=>A[0].map((k,i)=>A.map((v)=>v[i]));

実践

jsnoteに張り付けて, 実践してみた.
手元に用意したエクセルからcsvを吐き出して, importして実行してもいけた.
これでJavaScriptでグラフを描く準備ができた.

// read csv file in jsnote
// step1: push the button of import and select csv file
// step2: push the button of run or press the keys of "Shifit + Enter"

const transpose = A=>A[0].map((k,i)=>A.map((v)=>v[i]));
const csvParse = csv => csv
  .split(/\r\n|\n|\r/) //split by line feed codes
  .filter((k)=>k.match(/[^,\s]/)) //remove empty lines
  .map((k)=>k.trim() //remove white spaces of begining and end of line
    .replace(/,\s+/g,",") //remove white spaces
    .split(",") //split by cannma
    .map((l)=>isNaN(l)? l:parseFloat(l)) //convert string to flot
  ); 

const sample = 
  " 'title', 'tmp1', 'tmp2'\r\n \
          1,      1,     1 \r\n \
                           \r\n \
           ,       ,       \r\n \
          3,      4,     5 \r\n \
          5,      5,     5 \r\n \
          2,      3,     4 \r\n";

const csv = importTexts.length >0 ? importTexts[0].text : sample; //assign imported text or sample string to csv
console.log(csv);

const data = csvParse(csv);
console.log(data);

const index = data.shift(); //comment out tow lines from here
console.log(index);         //if index is not inclueded in csv

const dataT = transpose(data);
console.log(dataT);

参考