| |
| |
| |
| |
| 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; |
| }); |
| } |
|
|
| |
| |
| 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); |
| } |
| |