Skip to content

Instantly share code, notes, and snippets.

@rudfoss
Created March 26, 2024 10:40
Show Gist options
  • Save rudfoss/543c9b3ea2e048576c43c3eacc3ba5bc to your computer and use it in GitHub Desktop.
Save rudfoss/543c9b3ea2e048576c43c3eacc3ba5bc to your computer and use it in GitHub Desktop.
A small set of helper functions that convert raw data to CSV which in turn can be downloaded. Uses no libraries and escapes necessary characters.
const sanitizeCellValue = (value: unknown, cellSeparator: string): string => {
let valueString = String(value ?? "")
if (
valueString.includes(cellSeparator) ||
valueString.includes(" ") ||
valueString.includes('"') ||
valueString.includes("\n")
) {
valueString = valueString.replaceAll('"', '""')
valueString = `"${valueString}"`
}
return valueString
}
/**
* Given a data set will convert it to a csv string with a header and newline-separated rows.
* @param data The data to convert
* @param columns The name of each property to include. If not specified will infer properties based on the first row.
* @param cellSeparator The separator character to use between cells. Defaults to comma.
* @returns
*/
export const convertDataToCsvRows = <TRow extends object>(
data: TRow[],
columns?: Array<keyof TRow>,
cellSeparator: "," | ";" | "\t" | " " = ","
) => {
const columnsInOrder = columns ?? (Object.keys(data[0] ?? {}) as Array<keyof TRow>)
const columnNames = columnsInOrder
.map((columnName) => sanitizeCellValue(columnName, cellSeparator))
.join(cellSeparator)
if (data.length === 0) {
return [columnNames].join("\n")
}
const rows = data.map((row) =>
columnsInOrder
.map((columnName) => sanitizeCellValue(row[columnName], cellSeparator))
.join(cellSeparator)
)
return [columnNames, ...rows].join("\n")
}
/**
* Creates a url for downloading the CSV data provided as a file.
* @param csvData
* @returns A function that will trigger the download when called.
*/
export const makeDownloadableCsv = (
csvData: string,
filename: string,
type = "text/csv;charset=utf-8"
) => {
const blob = new Blob([csvData], { type })
const url = URL.createObjectURL(blob)
const downloadLink = document.createElement("a")
downloadLink.href = url
downloadLink.download = filename
downloadLink.style.visibility = "hidden"
return () => {
document.body.append(downloadLink)
downloadLink.click()
downloadLink.remove()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment