diff --git a/index.html b/index.html index 0626553..fbee56e 100644 --- a/index.html +++ b/index.html @@ -6,9 +6,9 @@ diff --git a/package.json b/package.json index 11ac613..0d4c7bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/utils", - "version": "0.22.2", + "version": "0.22.3", "description": "Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/csv.ts b/src/csv.ts index 81aec4d..87736db 100644 --- a/src/csv.ts +++ b/src/csv.ts @@ -1,4 +1,5 @@ -import {dotNotation, flattenObj} from './objects.ts'; +import {ASet} from './aset.ts'; +import {dotNotation, flattenObj, JSONAttemptParse, JSONSanitize} from './objects.ts'; /** * Convert an object to a CSV string @@ -7,20 +8,13 @@ import {dotNotation, flattenObj} from './objects.ts'; * @param {boolean} flatten Should nested object be flattened or treated as values * @return {string} CSV string */ -export function csv(target: any[], flatten=true) { - const headers = target.reduce((acc, row) => { - Object.keys(flatten ? flattenObj(row) : row) - .forEach(key => { if(!acc.includes(key)) acc.push(key); }); - return acc; - }, []); +export function toCsv(target: any[], flatten=true) { + const headers = new ASet(target.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], [])); return [ headers.join(','), ...target.map(row => headers.map((h: string) => { const value = dotNotation(row, h); - const type = typeof value; - if(type == 'string' && value.includes(',')) return `"${value}"`; - if(type == 'object') return `"${JSON.stringify(value)}"`; - return value; + return (typeof value == 'object' && value != null) ? '"' + JSONSanitize(value).replaceAll('"', '""') + '"' : value; }).join(',')) ].join('\n'); } diff --git a/src/objects.ts b/src/objects.ts index 76849a8..0560e0e 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -91,7 +91,6 @@ export function dotNotation(obj: any, prop: string, set?: T): T | undefined { }, obj); } - /** * Convert object into URL encoded query string * @@ -128,8 +127,9 @@ export function encodeQuery(data: any): string { export function flattenObj(obj: any, parent?: any, result: any = {}) { if(typeof obj === "object" && !Array.isArray(obj)) { for(const key of Object.keys(obj)) { - const propName = parent ? parent + '.' + key : key; - if(typeof obj[key] === 'object') { + const propName = parent ? `${parent}.${key}` : key; + if(typeof obj[key] === 'object' && obj[key] != null && !Array.isArray(obj[key])) { + console.log(propName, ); flattenObj(obj[key], propName, result); } else { result[propName] = obj[key]; @@ -216,11 +216,11 @@ export function mixin(target: any, constructors: any[]) { /** * Parse JSON but return the original string if it fails * - * @param {string} json JSON string to parse + * @param {any} json JSON string to parse * @return {string | T} Object if successful, original string otherwise */ -export function JSONAttemptParse(json: string): T | string { - try { return JSON.parse(json); } +export function JSONAttemptParse(json: T2): T1 | T2 { + try { return JSON.parse(json); } catch { return json; } }