要件
・DBに格納されているデータをCSVでダウンロードできるようにする
・データはユーザーが回答したアンケートの結果
使用したモジュール
・json2csv
https://www.npmjs.com/package/json2csv
開発
1. データの成形
今回は、ユーザーが回答したアンケート結果をCSVとして出力したいと思いますので、表示する項目をユーザー名
、メールアドレス
、アンケート設問
、回答結果
とします。
// DBから出力したいデータを取得する
const data = await getData.getUserData(user_id);
// データをJSON型に整える
const groupData = data.reduce((result, current) => {
// user_idごとにデータをまとめる
const element = result.find((p) => {
return p.id === current.user_id;
});
// user_idが既にあればquestionとanswerのみを追加する
if (element) {
element.data.push({
question: current.question,
answer: current.answer
});
} else { // user_idが切り替わった時に新規の要素としてgroupDataに追加する
result.push({
id: current.user_id,
name: current.user_name,
email: current.user_email,
data: [{
question: current.question,
answer: current.answer,
}],
});
}
return result;
}, []);
DBから取得したデータをJSONに成形しました。reduce関数を使っているのは1クエリで適切な形でデータを取得できなかったためなので、必須ではありません。for文やmap関数を使ってきれいにしてください。
reduce関数でのデータをグループ化は以下を参考にさせて頂きました。
参考サイト
JavaScript オブジェクト配列をsqlのgroup byのように集計する
成形したデータは以下のようになりました。dataの個数は同じでなくても大丈夫です。
[
{
id: 267,
name: "taro",
email: "taro@example.com",
data:[
{
question: "好きなプログラミング言語は?",
answer: ["HTML", "CSS", "JavaScript"]
},
{
question: "得意なプログラミング言語は?",
answer: ["HTML", "CSS"]
},
{
question: "苦手なプログラミング言語は?",
answer: ["JavaScript"]
}
]
},
{
id: 269,
name: "jiro",
email: "jiro@example.com",
data:[
{
question: "好きなプログラミング言語は?",
answer: ["Go", "JavaScript"]
},
{
question: "得意なプログラミング言語は?",
answer: ["PHP", "Ruby"]
},
{
question: "苦手なプログラミング言語は?",
answer: ["Java"]
}
]
},
]
2. JSONをCSVに変換
続いてJSONに成形したデータをCSVに変換していきます。今回はjson2csv
というモジュールを使用しました。他にもCSV系のモジュールはたくさんありましたので、自分にとって使いやすいものや仕様に沿っているものを選択すると良いかと思います。
追記:2020/09/27
以前、json2csvで出力したCSVファイルがExcelで文字化けしてしまうという記事内で、withBOM
オプションをつけるとExcelでも文字化けしなくなる、と書いていたのですがこの方法はExcel2016からが有効だったみたいで、それ以前のバージョンで文字化けしないようにするには単純にUTF-8ではなくShift-JISに文字コードを設定します。大変失礼いたしました。
Shift-JISへの変換はjconv
というモジュールを使用しました。
const {Parser, transforms: {unwind}} = require('json2csv');
const jconv = require('jconv'); // 追記:2020/09/27
function getCsv(data, fields, pathes, res) {
const transforms = [unwind({
paths: pathes,
blankOut: true,
})];
const json2csvParser = new Parser({fields, transforms});
const csv = data.length ? json2csvParser.parse(data) : '';
res.setHeader('Content-disposition', 'attachment; filename=data.csv');
res.setHeader('Content-Type', 'text/csv; charset=Shift_JIS');
return (jconv.convert(csv, 'UTF8', 'SJIS')); // 追記:2020/09/27
}
module.exports = getCsv;
引数のdata
は先程成形したデータが渡ってきます。fields
にはdataの中の表示させたい項目を配列で渡します。今回の例ですと、以下のようになります。
const fields = ['name', 'email', 'data.question', 'data.answer'];
questionとanswerはdataの入れ子になっているのでドット繋ぎにします。
最後のpathes
はdataの中でネストされている項目を配列で渡します。今回の例では以下のようになります。
const pathes = ['data']
ここで、pathesの中にdata.answer
を入れると、answerに格納されている配列が展開されて表示されるようになります。今回は、一つの設問に対して複数の回答を1行で表示させたかったのでdataのみにしました。
他にも色々オプションがありますので、詳しくは公式ドキュメントを参照してください。
3. CSVの出力
後は、CSVに変換したデータをPOSTなどのルーティングでres.send
してあげることで、ブラウザからファイルをダウンロードすることができます。
const csv = getCsv(groupData, fields, pathes, res);
res.send(csv);
以上です。