-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathload-csv.ts
More file actions
52 lines (41 loc) · 1.63 KB
/
Copy pathload-csv.ts
File metadata and controls
52 lines (41 loc) · 1.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { readFileSync } from "node:fs";
import Papa from "papaparse";
export type FieldType = "string" | "number" | "boolean";
export interface CsvData {
columns: string[];
rows: Record<string, string>[];
fieldTypes: Map<string, FieldType>;
}
export function loadCsv(path: string): CsvData {
const csvContent = readFileSync(path, "utf-8");
const { data: rows, meta } = Papa.parse<Record<string, string>>(csvContent, {
header: true,
skipEmptyLines: true,
transformHeader: (header: string) => header.trim(),
transform: (value: string) => value.trim(),
});
if (!meta.fields) {
throw new Error("CSV file has no header row");
}
const fieldTypes = new Map(inferFieldTypes(rows, meta.fields));
return { columns: meta.fields, rows, fieldTypes };
}
function inferFieldType(values: string[]): FieldType {
const nonEmptyValues = values.filter((v) => v !== "");
if (nonEmptyValues.length === 0) return "string";
const allBooleans = nonEmptyValues.every((v) => v === "true" || v === "false");
if (allBooleans) return "boolean";
const allNumbers = nonEmptyValues.every((v) => !Number.isNaN(parseFloat(v)) && Number.isFinite(Number(v)));
if (allNumbers) return "number";
return "string";
}
/**
* Infer the field types from the data in the CSV file.
* Returns the column name and the inferred field type.
*/
function inferFieldTypes(rows: Record<string, string>[], columns: string[]): [string, FieldType][] {
return columns.map((column) => {
const values = rows.map((row) => row[column] ?? "");
return [column, inferFieldType(values)];
});
}