オリジナルKdB(科目検索)を作ってみよう -3- データで遊ぶ編

### これまでの記事
- [オリジナルKdB(科目検索)を作ってみよう -1- Node.jsセットアップ編](https://qiita.com/tkkrr/items/2f0d93b6d0c758a88037)
- [オリジナルKdB(科目検索)を作ってみよう -2- パース編](https://qiita.com/tkkrr/items/d8a87b4f598b1b107266)

### 前回の要約
- KdBからCSVファイルをダウンロードした
- npmを使った基本的な開発フローを学んだ
- 文字列をパースした

<img width="1241" alt="スクリーンショット 2019-02-22 16.11.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/245014/3d0c6b57-d3a9-1be1-4166-f9068a2f9771.png">

# この記事を読む前に
この記事はシリーズものです。前回の記事を読んである前提で話を進めるので注意してください。
また、ある特定の人にしかわからない単語が出現する可能性が高いです。あらかじめご了承ください。

### この記事での開発環境
- MacOS 10.14.3
- Visual Studio Code 1.31.1
- Node.js LTS 10.15.1

# 今回やること
- javascriptで提供されているStreamAPIを使ってファイル読み込みをする。
- 読み込んできた文字列を扱いやすい文字コードに変換する。

キーワード **文字コード, ファイル読み込み, 高階関数**

# 実際のデータをフォルダに入れよう
javascript内で定義した文字列をパースして配列に変換することができた前回。そこから少しだけステップアップして、今回は実際にダウンロードしたファイルに対してパースしていきます！

まずは状況把握から

```shell
$ pwd #作業ディレクトリの確認
/Users/〇〇/myKdb #作業ディレクトリにいることを確認

$ ls
index.js			node_modules		kdb.csv
package-lock.json	package.json
```

KdBからダウンロードしてきたファイルをディレクトリに追加しています。前回書いた通り、このkdc.csvの中身は**Shift_JIS**で文字エンコーディングされていますので、表示すると以下のようになります。

```kdb.csv
"01AA007","�����Ȋw�I���j�o�X���u","1","1.0","1�E2","�HA","�W��","","���� �P��,�ɓ� �|��,�吼 �a�v,�J�� ����,�A�� �b�q,���� ��,�ז� ��,�גJ ����,���� ��,�ݞ� �_�u,�c�� �ؖȎq,�i�@ ��O�Y,���� ���݂�,���� �v�T,�牮 �ɏt","�����Ȋw��U�S���̑S�A�g��w�@�����ɂ��I���j�o�X�����̏W���u�`�ł���.���ꂼ��̋����������������ʂɊ�Â��������w�̍Ő�[�����ɂ��ďЉ��ƂƂ���,�����̌����̈Ӌ`�⌤���@�̌����Ɖ��p���ɂ��ču�`����.","02AU002�Ɠ������{�B
10/29-10/30","","","Omnibus Lecture in Biology","01AA007","�����Ȋw�I���j�o�X���u2006","2019-02-15 11:17:48" ... 
```
今回はこの状況を考慮して書いていきます。フローとしては **ファイル読み込み → 文字コード変換 → パース** となります。

# 実際のデータをパースしてみよう
では早速`index.js`を編集していきます。

```javascript:index.js
const fs = require('fs'); // Node.js 標準実装の File System Module を使います
const parse = require('csv').parse; // 前回の記述のショートハンド(動作は同じ)

const filename = 'kdb.csv' // 読み込むファイル名

// ①
const parser = parse( (erorr, data) => {
    console.log(data)
})

// ②
fs.createReadStream(__dirname + '/' + filename)
    .pipe(parser);
```

さて、それでは解説していきます。
前半部分で、新しく`fs`というNode.jsの標準パッケージを読み込みました。これを読み込むとファイル入出力関連の機能を使うことができます。また、しれっと2行目でショートハンド記述をしていますが、ここら辺は宗教になるので、どうしても気になる方は前回の記事の記述方法に修正して大丈夫です。どちらの記述方法でも問題なく先に進むことができます。

#### ① 未完成な関数
気づいた方もいるかもしれませんが、こちらの記述部分が前回の記述方法と少し違います。

```javascript:前回
parse( str, (erorr, data) => {
    console.log(data);
});
```

```javascript:今回
const parser = parse( (erorr, data) => {
    console.log(data)
})
```

説明が難しいのですが、前回は「**関数をすぐに実行していた**」のに対して、今回は「**関数を定義した**」のです。よくよくみてみると、前回は引数を2つ`parse()`に与えていましたが、**今回与えた引数は1つだけ**です。つまり、`parse()`は未完成な関数として関数内の処理を行わず、変数`parser`に格納されます。このように「関数を返す・関数を引数にとる関数」を**高階関数**といいます。

今回は`parse()`が関数ではなく**高階関数として機能**し、変数`parser`には**関数が格納されている**のです。

関数を格納した`parser`を実際に使うにはあと1つ足りない引数を渡せばOKです。つまり`parser("hoge,fuga")`とすれば`"hoge,fuga"`をパースしてくれます。

何となくわかったでしょうか？難しいですが先に進みましょう！

#### ② Stream API
何やらかっこいい名前が出てきました。ここでは「ファイル読み込みストリーム」を用意し、そのストリームに対して逐次処理をしています。

イメージとしては**原始的なろ過装置**です。流れてきた泥水にそれぞれ層でフィルターして最終的に綺麗な水を得るように、流れてきた単純な文字列をそれぞれの処理過程で加工して最終的に欲しいデータを得ると言うことです。(伝わったか...？)

```javascript
fs.createReadStream(__dirname + '/' + filename)
```
`fs.createReadStream()`は読んで字のまま、読み込みファイルストリームを作るという関数になります。このとき、引数でファイルまでのパスを与えます。

しれっと出てきた`__dirname`ですが、これはshellコマンドで言うところの`pwd`で表示される文字列(現在の作業ディレクトリまでのパス)に変換されます。なので、環境に左右されず普遍的なパス名を獲得することができる便利なキーワードです。`__dirname`では最後のスラッシュまではカバーしてくれないので`/`を文字列連結して、最後に`CSVファイル名`を連結します。

```javascript
fs.createReadStream(__dirname + '/' + filename)
    .pipe(parser);
```

次に、そのストリームに対して`parser`を施していきます。この時、引数を与えていないように見えますが、ファイルから読み込んできた文字列が自動的に引数に流れ込みます。内部的には`parser(流れ込んできた文字列)`という処理になります。

それでは実行してみましょう

```shell
$ node index.js
[
  [ '01AB255',
    '�A���זE��`���w���KI',
    '2',
    '3.0',
    '1',
    '�ʔN',
    '���k',
    '������',
    '',
    '�A���זE,�g�D�|�{����ш�`�q�H�w�Ɋւ�����O�̌����_�����u�ǂ���,���̓��e�ɂ��đ��݂ɓ��_��,���̗�������w�[�߂�����.',
    '01AB256�Ɠ������{�B\n�A�g��w�@�����Ɋ֘A����w���̂ݎ�u�\\',
    '�~',
    '���ƒS�������̔��f�ɂ��',
    'Seminar in Plant Cell Engineering I',
    '01AB255',
    '�A���זE��`���w���KI',
    '2014-08-13 21:34:20' ],
  [ '01AB255', ...
```

# ﾇｵｯ...！おのれ！文字コード！
そうでした、文字コードの処理をしていませんね...
それでは文字コードの処理に必要なパッケージをインストールしましょう。

```shell
$ npm install iconv-lite
```

インストールが終わったらindex.jsを修正していきます。

```javascript:index.js
const fs = require('fs');
const parse = require('csv').parse;
const iconv = require('iconv-lite'); // 文字コード変換用

const filename = 'kdb.csv'

const parser = parse( (erorr, data) => {
    console.log(data)
})

fs.createReadStream(__dirname + '/' + filename)
    .pipe(iconv.decodeStream('shift_jis')) // Shift_JIS から変換
    .pipe(parser);
```

では変更箇所を見ていきましょう

```javascript
fs.createReadStream(__dirname + '/' + filename)
    .pipe(iconv.decodeStream('shift_jis'))
    .pipe(parser);
```
変換用に`.pipe()`が一行追加されました。**セミコロンの有無には気をつけましょう**。

ここでは、流れてきた文字列を**Shift_JIS**から普遍的な文字コードに変換して、次の処理に流します。
次の`.pipe(parser)`は人間が読める文字列を受け取ってパースするので、今度こそ読める形式になっていると思います。

それでは実行しましょう

```shell
[
  [ '01AB256',
    '植物細胞遺伝情報学演習II',
    '2',
    '3.0',
    '2',
    '通年',
    '応談',
    '研究室', ...
```

# 実際のデータ読み込めた！
これにて第一部完ッ！といったところです！おつかれさまでした！
まだファイルから読み込んできた文字列をjavascriptで調理しやすいデータ形式に変換してきただけですが、扱えるデータにするまでの作業がめちゃめちゃ大変なことは実際多いので、最初の難関突破といっても過言ではありません！

次回は読み込めたデータを実際にブラウザに表示してみましょう！

## 今回のあとがき
今回はCSVという形式でしたが、javascriptと相性がそこまで良くないということもあって大変かと思います。一般的に使われているJSON形式はjavascriptとの親和性は最高です。というのも、JSONは**JavaScript Object Notation**の略称であり、気味の悪い拡張子どころか、javascriptの名前が入っているくらい潔いです。

JSON形式に対してはパッケージを1つもインストールすることなく`JSON.parse("JSONの:文字列")`でパースできますし、何の加工も必要なくObject型になるのでアレコレと処理しやすいです。

名門ならCSVとかTSVじゃなくてJSONでダウンロードさせてくれよ〜って思いますけど、たぶんよくわからないイザコザとかがあって簡単にはいかないんでしょう。

100歩譲ってCSV形式は軽量化できるので許せるとしても、Excel形式をダウンロードさせるのは謎

### 参考サイト
- [山本隆の開発日誌](https://www.gesource.jp/weblog/?p=7653)
- [CSV.js](https://csv.js.org)
- [JSON | MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON)

次の記事 → https://qiita.com/tkkrr/items/5cafa77c104b11d76a16