Node.js を使ってファイルからデータを読み取ってみた。

#はじめに
この記事はなんとなくJSとかを勉強している学生がメモ代わりに記録しているものです。内容は期待しないでください。

#仮想環境を開く
今回はUbuntuで行うのでiTerm2で仮想環境を起動する。

起動したところで
1.Virtual Box(バーチャルボックス)
2.Vagrant（ベイグラント）
という2つのソフトウェアを使った仮想環境でUbuntuを使用します。
cd ~/vagrant/ubuntu
vagrant up
vagrant ssh

Ubuntuがインストールされたディレクトリに移動。vagrant upは仮想的なPCにインストールされたUbuntuを起動するコマンドで,vagrant sshはVagrantの仮想マシンがセットされている状態でSSHに接続します。

#1.ファイルから読み取るプログラムを書く
```javascript

'use strict';
const filesystem=require('fs');
const readline=require('readline');
const rs =fs.createReadStream('ファイル名');
const rl=readline.createInterface({input:rs, output:{}});
const prefectureDataMap = new Map();
rl.on('line', lines =>{
  console.log(lines);
});
```
1行目はJSをstrictモードで利用するための記述です。

2,3行目はNode.js内のモジュールをrequireで呼び出しています。requireとは,拡張機能を使用することができるnpm(Node Package Manager)モジュールです。以下の記述でモジュールを変数に代入してJS内で使用することができます。
```const 変数名 = require('モジュール名')
```
今回呼び出したモジュールはファイルを扱うためのfs(filesystem)とファイルを1行ずつ読み込むreadlineです。

4行目はストリームを使用してファイルを読み込み,rsという変数に代入しています。この他にもfsモジュールにはfs.readFileSyncなどの同期的にデータを読み込む関数も存在します。

5行目はストリームを行ごとに読み込むことができるモジュール(readline)を使用して以下のように設定しています。
```//インターフェースの設定
const rl = readline.createInterface({
//読み込みたいストリームの設定
  input: rs,
//書き出したいストリームの設定
  output: {}
});
```
6行目はキーと値を組み合わせて扱うためMapオブジェクトを使用しています。ここでは変数にMapオブジェクトを代入するとともに,新しくMapオブジェクトを初期化しています。

7行目はrlというオブジェクトでlineというイベントが起きた時に以下の関数を発火させています。今回はlinesという引数をコンソールに表示しています。

#2.6行目の関数を変更する
6行目のonメソッドに渡されている関数を以下に変更しました。
```rl.on('line', lines => {
  const cutline = lines.split(',');
  const year = parseInt(cutline[0]);
  const prefecture = cutline[1];
  const popu = parseInt(cutline[3]);
 if (year === 2010 || year === 2015) {
    let vofmap = prefectureDataMap.get(prefecture);
    if (!vofmap) {
      vofmap = {
        p10: 0,
        p15: 0,
        change: null
      };
    }
    if (year === 2010) {
      vofmap.p10 = popu;
    }
    if (year === 2015) {
      vofmao.p15 = popu;
    }
    prefectureDataMap.set(prefecture, value);
  }
});
```
2行目のsplit()メソッドは、Stringを指定した区切り文字列で分割することにより、文字列の配列に分割します。メソッドの使い方は以下のように使います。
```文字列.sprit(指定した区切り)
```

3行目からは文字列から作られた配列の一部を変数に代入しています。parseInt()は文字列の引数を解析し,指定された基数 (数学的記数法の底) の整数値を返します。簡単にいうと文字列の値だったものを数字(整数値)に変更し,扱いやすくしています。

6行目からはif文を使用して指定した値を変数が持っている場合のみ以下のプログラムを動かしています。

７行目はマップにキーを指定しながらキーの値を取り出しています。記述の仕方は以下参照
```マップ名.get(キー名);
```

8行目はキー名に指定された値がない場合,初めてキー名に当たる値を代入しています。

９行目以降のこの部分について解説します。
```if (year === 2010) {
      vofmap.p10 = popu;
    }
    if (year === 2015) {
      vofmao.p15 = popu;
    }
prefectureDataMap.set(prefecture, value);
  }
});
```
ここでは先ほどMapオブジェクトで作成したキー名に当たる値の各オブジェクトに代入しています。その後キーと値のセットを先ほどのマップに登録しています。記述の仕方は以下参照
```javascript

マップ名.set(キー名,値)
```

#3.closeで発火させる関数を記述する
以下の内容を最後に記述します。
```rl.on('close', () => {
  for (let [key, vofmap] of prefectureDataMap) {
    vofmap.change = vofmap.popu15 / vofmap.popu10;
  }
  const rankingArray = Array.from(prefectureDataMap).sort((pair1, pair2) => {
    return pair2[1].change - pair1[1].change;
  });
  const rankingStrings = rankingArray.map(([key, value]) => {
    return (
      key +
      ': ' +
      value.popu10 +
      ':' +
      value.popu15 +
       ':'+
      value.change
    );
  });
  console.log(rankingStrings);
});
```

１行目は先ほどと同じでrlというオブジェクトでcloseというイベントが起きた時に以下の関数を発火させています。

2~4行目はfor~of構文でprefectureDataMapというマップに対してキー名とそれに当たる値のループを行っています。その後に新たにchangeというオブジェクトをマップ内に作り当てはまる値を代入しています。

５行目は関数を行った後に配列に変換しています。今回はsortを使ってコールバック関数の値の大きさによって順番を変更しています。記述方法は以下参照。
```Array.from(変換対象,関数{})
```

９行目はmap関数を使っています。先ほどのキー名などを使うものとは別物です。map関数は、 Array の要素それぞれを,与えられた関数を適用した内容に変換します。今回は全てを文字列として変換しています。

```
node JSファイル名
```
以上を実行してREPLでエラー等が発生しなければ成功です。お疲れ様でした！！


##ストリームとは
サイズが非常に大きなデータを取り扱う際などには,データの全てを一度にメモリに読み込んでしまうとメモリが逼迫し,プログラムのパフォーマンスを低下させてしまう可能性があります。
そのような場合にはストリームを使用します。ストリームとはデータを徐々に読み込んだり,書き込んだり、変換したりすることができるものです。