1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

csv-parse で TSV/CSV をパースするときに型変換を行う

Last updated at Posted at 2024-05-16

前提

TSV ファイルを DB に insert するとき、 csv-parse というモジュール1を使ってパースを行いました。
ただその際、デフォルトでは数値や真偽値もキャストされず String として読み込まれてしまいます。

方法

cast オプションにキャストを行うコールバック関数を書いて渡します。
コールバック関数は value と context を引数として受け取ります。
context が持っている様々なプロパティを使って列を指定し、value をキャストして値を return します。
なお、今回は TSV ファイルを扱っていますが、 CSV ファイルについても同様です。

環境

Bun 1.1.3

ファイル構成

|--index.ts
|--joyokanji.tsv
joyokanji.tsv
id	grapheme_orth	grapheme_alt	grapheme_olds	radicals	stroke_count	grade	is_new	onyomis	kunyomis
1	亜		亞	二	7	S	false	ア	
2	哀			口	9	S	false	アイ	あわ-れ,あわ-れむ
3	挨			手	10	S	true	アイ	
4	愛			心	13	4	false	アイ	
5	曖			日	17	S	true	アイ	
6	悪		惡	心	11	3	false	アク,オ	わる-い

https://ja.wikipedia.org/wiki/常用漢字一覧 から取ってきて適当に加工したものです。

sh
bun add csv-parse

npm install とか yarn addとか。
忘れがちですがやっておきましょう。

コード例

例0: オプションに cast を指定しない場合

index.ts
import fs from 'fs';
import { parse } from 'csv-parse/sync';

const file = fs.readFileSync('joyokanji.tsv', 'utf8');
const options = {
  columns: true,
  delimiter: '\t'
};
const records = parse(file, options);

console.log(records);

実行結果

[
  {
    id: "1",
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: "7",
    grade: "S",
    is_new: "false",
    onyomis: "",
    kunyomis: "",
  }, {
    id: "2",
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: "9",
    grade: "S",
    is_new: "false",
    onyomis: "アイ",
    kunyomis: "あわ-れ,あわ-れむ",
  }, {

    ...

    
  }, {
    id: "6",
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: "11",
    grade: "3",
    is_new: "false",
    onyomis: "アク,オ",
    kunyomis: "わる-い",
  }
]

value がすべて String になっています。

例1: 列名によって型変換を行う

今回対象としたファイルにはヘッダー列があります。
こういった場合、そもそもオプションで columns: true を指定する必要がありますが、さらに cast オプション内の context.column で列名を取得・検査することで、任意の列を任意の型にキャストできます。
今回は idstroke_count 列をInteger型に、 is_new 列を Boolean型にしたいので、
switch 文を使って context.column を検査し、それぞれの value をキャストして return しています。

ts.index.ts
import fs from 'fs';
import { parse } from 'csv-parse/sync';

const file = fs.readFileSync('joyokanji.tsv', 'utf8');
const options = {
  columns: true,
  delimiter: '\t',
  cast: function(value, context) {
    switch(context.column) {
      case 'id':
      case 'stroke_count':
        return parseInt(value, 10);
      case 'is_new':
        return value.toLowerCase() === 'true';
      default:
        return value;
    }
  }
};
const records = parse(file, options);

console.log(records);

実行結果

[
  {
    id: 1,
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: 7,
    grade: "S",
    is_new: false,
    onyomis: "",
    kunyomis: "",
  }, {
  
    ...
    
  }, {
    id: 5,
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: 17,
    grade: "S",
    is_new: true,
    onyomis: "アイ",
    kunyomis: "",
  }, {
    id: 6,
    grapheme_orth: "",
    grapheme_alt: "",
    grapheme_olds: "",
    radicals: "",
    stroke_count: 11,
    grade: "3",
    is_new: false,
    onyomis: "アク,オ",
    kunyomis: "わる-い",
  }
]

キャストしてパースできました。

ちなみにヘッダー列がない場合は、例えば context.index プロパティで列インデックスを指定してキャストするといいと思います。

余談

みんな大好き Papa Parse には dynamicTyping というオプションが用意されていて、これを true にしておくだけで、よしなに型変換を行ってくれるそうです。
モジュールの選定は大事ですね。

  1. csv-parse は csv モジュールにも含まれているので、 csv を install/add することでも利用できます。csv は csv-generate, csv-parse, stream-transform, そして csv-stringify の4つのモジュールのパッケージです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?