質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

2回答

6462閲覧

Node.jsでCSVファイルを読み込み、データを加工して配列の形のまま返す方法を知りたい

kinoki

総合スコア2

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2021/12/27 11:31

前提・実現したいこと

Node.jsでCSVファイルを読み込み、必要な情報だけに加工して配列の形にするプログラムを書きたいです。

(例)

  1. data.csvを読み込む
  2. 変数に検索したいワードが入っているので、一致する内容の行や指定した列のみの形にして配列にする

(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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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

umau

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問