provinans / frontend /src /csv.ts
reversely's picture
Upload folder using huggingface_hub
c9a1ce7 verified
Raw
History Blame Contribute Delete
2.69 kB
// Minimal CSV parse for a client-side dictionary upload (#TBD). Handles quoted
// fields, escaped quotes (""), embedded commas and newlines, and CRLF. Returns
// one object per data row keyed by the trimmed header cells; blank lines are
// skipped. No library needed for a dictionary of a few hundred variables.
export function parseCsv(text: string): Record<string, string>[] {
const rows: string[][] = [];
let field = "";
let row: string[] = [];
let inQuotes = false;
const s = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (inQuotes) {
if (c === '"') {
if (s[i + 1] === '"') {
field += '"';
i += 1;
} else {
inQuotes = false;
}
} else {
field += c;
}
} else if (c === '"') {
inQuotes = true;
} else if (c === ",") {
row.push(field);
field = "";
} else if (c === "\n") {
row.push(field);
rows.push(row);
row = [];
field = "";
} else {
field += c;
}
}
if (field.length > 0 || row.length > 0) {
row.push(field);
rows.push(row);
}
if (rows.length === 0) return [];
const header = rows[0].map((h) => h.trim());
return rows
.slice(1)
.filter((r) => r.some((cell) => cell.trim() !== ""))
.map((r) => {
const record: Record<string, string> = {};
header.forEach((h, idx) => {
record[h] = (r[idx] ?? "").trim();
});
return record;
});
}
// Minimal CSV serialization for client-side export downloads. No library
// needed for data this small (a few hundred rows, flat objects).
export function toCsv<T extends object>(rows: T[]): string {
if (rows.length === 0) return "";
const columns = Array.from(
rows.reduce((set, row) => {
Object.keys(row).forEach((k) => set.add(k));
return set;
}, new Set<string>()),
);
const escape = (value: unknown): string => {
if (value === null || value === undefined) return "";
const s = String(value);
return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
};
const lines = [columns.join(",")];
for (const row of rows) {
const record = row as Record<string, unknown>;
lines.push(columns.map((c) => escape(record[c])).join(","));
}
return lines.join("\n");
}
export function downloadCsv<T extends object>(filename: string, rows: T[]): void {
const blob = new Blob([toCsv(rows)], { type: "text/csv;charset=utf-8;" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}