前提・実現したいこと
Node.jsでCSVファイルを読み込み、必要な情報だけに加工して配列の形にするプログラムを書きたいです。
(例)
- data.csvを読み込む
- 変数に検索したいワードが入っているので、一致する内容の行や指定した列のみの形にして配列にする
(data.csvの中身)
日付, 名前, 個数, 担当
1223, あいう, 1, A
1224, かきく, 2 ,B
1224, さしす, 3, C
1224, たちつ, 4, C
(期待する出力)
検索ワード:1224(日付), C(担当) ,[出力する列]名前・個数
さしす, 3
たちつ, 4
該当のソースコード
js
1// csvファイルの読み込み 2const fs = require('fs'); 3const csv = require('csv'); 4fs.createReadStream(__dirname +'/data.csv').pipe(csv.parse(function(err, data) {})); 5 6searchword_1 = 1224 7searchword_2 = C 8column_1 = '名前' 9column_2 = '日付'
試したこと
読み込みまで・検索した結果CSV出力する内容のものが多くどう書けばいいのか結局わかりませんでした。
初心者なので見逃しや見当違いな表現などあるかもしれませんがお手柔らかにお願いします。
よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
質問文のコードはNode.js最難関であるStreamの機能を使ったコードなので、
プログラミング初心者が使うにはかなり厳しいものがあります。
そもそもなんでStreamなんて使っているのかというと、
CSVファイルの中には郵便番号一覧みたいなやべえのがあって
これを複数の変数でやり取りしているとアホみたいにメモリ容量を食うからですね。
なので1行ずつスコップですくいあげて、関数に流して処理……みたいなエコな対策が必要となります。
まぁ、数百MB行くような超巨大なCSVファイルなんて遭遇しないので、
普通に同期処理でやれば良いのです。
このcsvモジュールのドキュメントでは
CSVParse for Node.js の Sync APIページに同期処理版も同梱しているから
用途に応じてこっちも使ってねみたいに紹介されています。
理解しやすさ重視でこちらを使って回答していきます。
bash
1$ npm install csv-parse
js
1const fs = require('fs'); 2const {parse} = require('csv-parse/sync'); 3 4const text = fs.readFileSync(__dirname + '/data.csv').toString(); 5 6const csv = parse(text, { 7 columns: true, 8}); 9console.log(csv); 10// [ 11// { '日付': '1223', '名前': 'あいう', '個数': '1', '担当': 'A' }, 12// { '日付': '1224', '名前': 'かきく', '個数': '2', '担当': 'B' }, 13// { '日付': '1224', '名前': 'さしす', '個数': '3', '担当': 'C' }, 14// { '日付': '1224', '名前': 'たちつ', '個数': '4', '担当': 'C' } 15// ] 16 17const csv2 = parse(text); 18console.log(csv2); 19// [ 20// [ '日付', '名前', '個数', '担当' ], 21// [ '1223', 'あいう', '1', 'A' ], 22// [ '1224', 'かきく', '2', 'B' ], 23// [ '1224', 'さしす', '3', 'C' ], 24// [ '1224', 'たちつ', '4', 'C' ] 25// ]
大体望んでいた配列そのままなんじゃないですか?
要件はもう一個ありましたね。
(期待する出力)
検索ワード:1224(日付), C(担当) ,[出力する列]名前・個数
さしす, 3
たちつ, 4
複数条件のANDで検索したいと言ってるわけですね。
js
1searchword_1 = 1224 2searchword_2 = C 3column_1 = '名前' 4column_2 = '日付'
こういう風に個別の変数を大量に用意するやり方は、
コードがクソ長くなるので非推奨です。
ここは配列で定義→for文で一括処理
この流れを意識しましょう。
js
1const conditions = [ 2 {column: "日付", word: "1224"}, 3 {column: "名前", word: "C"}, 4];
こんな感じかな?
あとはさっきのコードに合体させる感じにして
for文の多重ループになりますね。
js
1const fs = require('fs'); 2const {parse} = require('csv-parse/sync'); 3 4const text = fs.readFileSync(__dirname + '/data.csv').toString(); 5const csv = parse(text, { 6 columns: true, 7}); 8 9const conditions = [ 10 {column: "日付", word: "1224"}, 11 {column: "名前", word: "さしす"}, 12]; 13 14const hits = []; 15for (const datum of csv) { 16 // AND条件を表現するため、最初はHit扱い、条件外なら除外する 17 let isHit = true 18 for (const {column, word} of conditions) { 19 if (datum[column] !== word) { 20 isHit = false; 21 break; 22 } 23 } 24 if (isHit) { 25 hits.push(datum); 26 } 27} 28 29console.log(hits); 30// [ { '日付': '1224', '名前': 'さしす', '個数': '3', '担当': 'C' } ]
これで解決したかと思いますが、
冒頭で触れたStream版は高速なシステムを作るためには扱えて損はありませんので、
よりチャレンジングな課題として取り組んでみても良いと思います。
投稿2021/12/28 06:00
編集2021/12/28 06:04総合スコア21203
0
もっと良い書き方ある気もしますが、、
javascript
1const fs = require('fs'); 2const csv = require('csv'); 3 4const searchword_1 = "1224" 5const searchword_2 = "C" 6 7fs.createReadStream(__dirname +'/data.csv').pipe(csv.parse(function(err, data) { 8 const header = data.splice(0, 1)[0]; 9 10 if (searchword_1) { 11 data = data.filter(row => row[header.indexOf('日付')] === searchword_1); 12 } 13 14 if (searchword_2) { 15 data = data.filter(row => row[header.indexOf('担当')] === searchword_2); 16 } 17 18 data.forEach(row => { 19 console.log(`${row[header.indexOf('名前')]},${row[header.indexOf('個数')]}`) 20 }) 21})); 22
※記載されているdata.csvの内容には空白が入ってますが、このコードは空白入ってない想定です。(普通、CSVデータって空白もデータの一部とみなすものだと思うで。)
日付,名前,個数,担当 1223,あいう,1,A 1224,かきく,2,B 1224,さしす,3,C 1224,たちつ,4,C
※「csv.parse」のコールバック関数から外に出したくて、async/awaitを使った実装も作ってみたんですが(参考)、余計にややこしくてあまりキレイじゃないので、上記の方がマシかもしれません。StreamAPI難しいですね。
参考に一応貼っておきます。↓
javascript
1 2const fs = require('fs'); 3const csv = require('csv'); 4 5const searchword_1 = "1224" 6const searchword_2 = "C" 7 8const reader = async (rs) => { 9 const list = [] 10 for await (const row of rs) { 11 list.push(row); 12 } 13 return list; 14} 15 16(async function() { 17 const rs = fs.createReadStream(__dirname +'/data.csv').pipe(csv.parse()); 18 let rows = await reader(rs); 19 20 const header = rows.splice(0, 1)[0]; 21 22 if (searchword_1) { 23 rows = rows.filter(row => row[header.indexOf('日付')] === searchword_1); 24 } 25 26 if (searchword_2) { 27 rows = rows.filter(row => row[header.indexOf('担当')] === searchword_2); 28 } 29 30 const result = []; 31 32 rows.forEach(row => { 33 result.push([row[header.indexOf('名前')], row[header.indexOf('個数')]]); 34 }); 35 36 console.log(result) 37})(); 38
投稿2021/12/27 15:00
総合スコア831
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。