JavaScriptでJSONファイルを同ディレクトリ上に生成する

結論からいうとnodeを使って生成します。fsモジュールを使います。
「JavaScript JSONファイル 生成」とJavaScriptを使ったキーワードでググったところ某スクールの違う、そうじゃない、な記事やあまり関係のない記事が出てきてしまったため対抗馬としてJavaScriptを前面に押して記事にしました。（結局は他の記事と同じようにnodeを使いますが）

特に初学者は「ググっても出てこないじゃないか」、と困っているかと思います。記事の前提をクリアして手を動かしてみてください。

追記：もっと良い書き方あれば教えてもらえるとハッピーになります。

## 【この記事の前提】 nodeをインストールしよう
nodeとはサーバーサイドのJSです。ブラウザ以外のところでもJavaScriptを実行できるよ、という実行環境になります。
nodeとセットで語られるnpmは聞いたことがある人が多いと思いますがnodeで動く様々なモジュールを引っ張ってくるためのパッケージマネージャーです。

### nodeをインストール
初心者は以下を参考に導入すると良いかと思います。
<a href="https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09">MacにNode.jsをインストール</a>
<a href="https://qiita.com/Masayuki-M/items/840a997a824e18f576d8">windows10にNode.jsをインストールする</a>

インストールの仕方はいくつかあり、今後JSフレームワークを使いたい人はanyenv経由でnodenvを導入してnodeのバージョン切り替えを楽にプロジェクトに応じてできるようにしておくと良いかと思います。
<a href="https://qiita.com/silmisilon/items/a2cc637eb7e59fa13f2c">anyenv から入れた nodenv で Node.js を入れたときのメモ</a>
<a href="https://qiita.com/silmisilon/items/a2cc637eb7e59fa13f2c">MacにNode.jsをインストール（anyenv + nodenv編）</a>
↑環境構築に少しは慣れている人向けです。自分はシェルをzsh環境へ変えるところから沼にハマってしまいしかもつまづいたところをメモに残してなかったのでどうやったのか思い出せない(バージョン切り替えできなくて残念でした)

```
node -v
```
でバージョンが表示されればOKです。

## 任意のディレクトリに今回のためのファイルを用意

```
% mkdir createObj //createObjフォルダを作る
% cd createObj //移動する
% touch create.js //jsファイルを作る
```


## 最小単位でfsモジュールを使ってJSONファイルを生成する
fsモジュールはnodeパッケージマネージャ(npmやyarn)からインストールしてこなくても最初からnodeの機能として備わっているためすぐに使うことができます。
create.jsのファイル編集します。

```create.js
const fs = require('fs');
const testObj = {
  test: 'dammy',
};

const createFile = (pathName, source) => {
  const toJSON = JSON.stringify(source);
  fs.writeFile(pathName, toJSON, (err) => {
    if (err) rej(err);
    if (!err) {
      console.log('JSONファイルを生成しました');
    }
  });
};

createFile('newObj.json', testObj);
```
fs.writeFileでpathNameで指定したファイルを生成しています。
コマンドライン(ターミナル)で node create で実行すると newObj.json のファイルが同ディレクトリ上で生成されます。

```
% node create 
```

```newObj.json
{"test":"dammy"}
```

JSONの中身を変えて再度実行するとファイルが上書きされます。

```create.js
const testObj = {
  test: 'second dammy',
};
```

## 工夫１ ファイルのディレクトリ場所を変える
下の形で相対パスを指定すればOKです。

```
createFile('./任意のフォルダ/newObj.json', testObj);
```

## 工夫２ ファイルを上書き保存されないようにする
ディレクトリチェックを行えるfs.statSync()メソッドを用いてファイル重複を確認します。
同ディレクトリ上に既存ファイルがあった場合はreturnでメッセージを返します。

```create.js
const fs = require('fs');
const testObj = {
  test: 'dammy',
};

const createFile = (pathName, source) => {
  const isExist = dupliCheck(pathName); //booleanを挟む
  if(isExist) return console.log('既存のパスが見つかりました'); //エラーメッセを返す

  const toJSON = JSON.stringify(source);
  fs.writeFile(pathName, toJSON, (err) => {
    if (err) throw err;
    if (!err) {
      console.log('JSONファイルを生成しました');
    }
  });
};

//新たに追加した関数
const dupliCheck = (pathName) => {
  try {
    fs.statSync(pathName); 
    return true
  } catch(err) {
    //パスが存在しない場合エラーを返す
    if(err.code === 'ENOENT') return false
  }
};

createFile('new.json', testObj);
```


## 工夫３ データ加工してJSONファイルを生成する
僕はこれがやりたくてfsモジュールを使いました。
エクセルからデータを加工すると手間がかかるので既存オブジェクトの値を元に計算した結果をオブジェクトに格納し生成したかったです。

ここでサンプルを用意しました。（なお、手っ取り早くデータが欲しいのでベストプラクティスみたいなところは意識してないんでそこらへんは許して欲しいンゴ。独学者のやり方なので良い方法あったら教えてください。)

### サンプルデータの簡単な説明
例として食材データObjectに含む栄養データが１日あたりの摂取する推奨量に対してどのくらい割合を満たしているのかを算出したものをJSONデータとしてアウトプットします(dailyRationSample.jsonとして)

### 外部データを用意する

```
% mkdir seed
% touch seed/sampleStuff.js
% touch seed/sampleDaily.js
```


module.exports で外部データとして引っ張ってこれるようにしています。

```sampleStuff.js
module.exports = [
  {
    id: 1,
    Name: 'アマランサス 玄穀 ',
    Protein: 12.7,
    Iron: 9.4,
  },
  {
    id: 2,
    Name: 'あわ 精白粒 ',
    Protein: 11.2,
    Iron: 4.8,
  },
  {
    id: 3,
    Name: 'あわ あわもち ',
    Protein: 5.1,
    Iron: 0.7,
  },
];


```


```sampleDaily.js
module.exports = [
  {
    name: 'Protein',
    volume: 60,
  },
  {
    name: 'Iron',
    volume: 7.5,
  },
];

```

### create.jsにデータを取り込んでオブジェクトを生成してファイルとしてアウトプットする
sampleStuff.jsの食材データの栄養素であるプロテイン(Protein)と鉄分(Iron)を sampleDaily.js の１日あたりの推奨量の大きさである volume とで割って割合を出してみましょう。

```create.js
const fs = require('fs');
const stuff = require('./seed/sampleStuff'); //外部データを引っ張っている
const daily = require('./seed/sampleDaily'); //外部データを引っ張っている

//新たに追加した関数
const createObj = () => {
  const objArray = [],
    directArr = ['id','Name'],
    divisionArr = ['Protein','Iron'];

  stuff.map(stuffObj => {
    const singleObj = {};
    directArr.map(keyName => {
      singleObj[keyName] = stuffObj[keyName];
    });
    divisionArr.map(keyName => {
      const dailyObj = daily.find(item => keyName === item.name);
      const { volume: denomi } = dailyObj, //分割代入したvolumeの名前をdenomi(分母)にしている
        molecule = stuffObj[keyName], //molecleは分子
        ratio = Math.round(molecule / denomi * 1000) / 1000;
      singleObj[keyName] = ratio;
    });
    objArray.push(singleObj);
  });

  return objArray;
}

const createFile = (pathName) => {
  const isExist = dupliCheck(pathName);
  if(isExist) return console.log('既存のパスが見つかりました');
  const targetObj = createObj();
  const toJSON = JSON.stringify(targetObj, null, 4); //行数が多いのでオプション指定して改行しておく
  fs.writeFile(pathName, toJSON, (err) => {
    if (err) throw err;
    if (!err) {
      console.log('JSONファイルを生成しました');
    }
  });
};

const dupliCheck = (pathName) => {
  try {
    fs.statSync(pathName);
    return true
  } catch(err) {
    if(err.code === 'ENOENT') return false
  }
};

createFile('dailyRatioSample.json');
```

上記を実行するとdailyRatioSample.jsonが生成されます。

```dailyRatioSample.json
[
    {
        "id": 1,
        "Name": "アマランサス 玄穀 ",
        "Protein": 0.212,
        "Iron": 1.253
    },
    {
        "id": 2,
        "Name": "あわ 精白粒 ",
        "Protein": 0.187,
        "Iron": 0.64
    },
    {
        "id": 3,
        "Name": "あわ あわもち ",
        "Protein": 0.085,
        "Iron": 0.093
    }
]
```

## 蛇足（記事更新）
JSON整形は別記事で説明しようと思いましたがやっぱりここで説明
下記のようにJSON.stringfy(オブジェクト, 置換オプション, 字下げ数)を指定して整形ことができます。

```
const toJSON = JSON.stringify(targetObj, null, 4);
つまり
const toJSON = JSON.stringify(オブジェクト, 置換オプション, 字下げ数);
```

「置換オプションってなんだよ」というところですが僕も使ったことがありません。（使うタイミングにまだ巡り合っていない）
<a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify">JSON.stringify() - MDN Web Docs - Mozilla</a> のサイトで構文を確認すると

```
JSON.stringify(value[, replacer[, space]])
```

となっています。また下の記事の引用では下記のような説明となっています。

"もし関数である場合、文字列化の間に出会った値とプロパティを変換します。もし配列である場合は、最終的な文字列のオブジェクトに含まれるプロパティの集合を指定します。" 引用：<a href="https://qiita.com/qoAop/items/57d35a41ef9629351c3c">JSON.stringifyを改めて調べる。</a>

JSONに含まれる関数と配列を置き換えできるようです。