Compare commits

..

5 Commits

Author SHA1 Message Date
49a2df8cd4 Fixed timezone format
All checks were successful
Build / Build NPM Project (push) Successful in 1m15s
Build / Tag Version (push) Successful in 13s
Build / Publish Documentation (push) Successful in 1m57s
2024-11-14 22:58:24 -05:00
ccf3fcb043 fixed fromCsv escaping quotes & letter headers
All checks were successful
Build / Build NPM Project (push) Successful in 37s
Build / Tag Version (push) Successful in 7s
Build / Publish Documentation (push) Successful in 1m46s
2024-11-09 17:27:05 -05:00
341ef37205 Added fromCsv
All checks were successful
Build / Build NPM Project (push) Successful in 37s
Build / Tag Version (push) Successful in 7s
Build / Publish Documentation (push) Successful in 1m43s
2024-11-09 16:30:05 -05:00
0eab2630ad added fileText method
All checks were successful
Build / Build NPM Project (push) Successful in 37s
Build / Tag Version (push) Successful in 6s
Build / Publish Documentation (push) Successful in 2m21s
2024-11-09 15:35:05 -05:00
2f59a9d02e improved toCSV
All checks were successful
Build / Build NPM Project (push) Successful in 41s
Build / Tag Version (push) Successful in 7s
Build / Publish Documentation (push) Successful in 1m41s
2024-11-09 13:49:04 -05:00
7 changed files with 79 additions and 55 deletions

View File

@ -6,9 +6,14 @@
</head> </head>
<body> <body>
<script type="module"> <script type="module">
import {PathEvent} from './dist/index.mjs'; import {fromCsv, toCsv} from './dist/index.mjs';
console.log(PathEvent.has(['data/Inventory:n', 'logs:c'], `logs/client:n`)); const csv = '_id,any,boolean,date,file,foreignKey,formula,javaScript,number,string,_createdBy,_createdDate,_updatedBy,_updatedDate\n' +
'34,,true,,,,,,,,system,2024-11-09T12:06:50.023Z,system,2024-11-09T17:44:42.512Z\n' +
'37,,,,,,,,,,system,2024-11-09T18:53:21.793Z,system,2024-11-09T18:53:21.793Z\n' +
'38,,,,,,,,,,system,2024-11-09T18:53:21.796Z,system,2024-11-09T18:53:21.796Z';
console.log(fromCsv(csv));
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
{ {
"name": "@ztimson/utils", "name": "@ztimson/utils",
"version": "0.22.2", "version": "0.22.7",
"description": "Utility library", "description": "Utility library",
"author": "Zak Timson", "author": "Zak Timson",
"license": "MIT", "license": "MIT",

View File

@ -1,4 +1,41 @@
import {dotNotation, flattenObj} from './objects.ts'; import {ASet} from './aset.ts';
import {dotNotation, flattenObj, JSONSanitize} from './objects.ts';
import {LETTER_LIST} from './string.ts';
export function fromCsv<T = any>(csv: string, hasHeaders=true): T[] {
const row = csv.split('\n');
let headers: any = hasHeaders ? row.splice(0, 1)[0] : null;
if(headers) headers = headers.match(/(?:[^,"']+|"[^"]*"|'[^']*')+/g);
return <T[]>row.map(r => {
function parseLine(line: string): (string | null)[] {
const parts = line.split(','), columns: string[] = [];
let quoted = false;
for(const p of parts) {
if(quoted) columns[columns.length - 1] = columns.at(-1) + ',' + p;
else columns.push(p);
if(/[^"]"$/g.test(p)) {
quoted = false;
} else if(/^"[^"]/g.test(p)) {
quoted = true;
}
}
return columns;
}
const props = parseLine(r);
const h = headers || (Array(props.length).fill(null).map((r, i) => {
let letter = '';
const first = i / 26;
if(first > 1) letter += LETTER_LIST[Math.floor(first - 1)];
letter += LETTER_LIST[i % 26];
return letter;
}));
return h.reduce((acc: any, h: any, i: number) => {
dotNotation(acc, h, props[i]);
return acc;
}, {});
})
}
/** /**
* Convert an object to a CSV string * Convert an object to a CSV string
@ -7,20 +44,13 @@ import {dotNotation, flattenObj} from './objects.ts';
* @param {boolean} flatten Should nested object be flattened or treated as values * @param {boolean} flatten Should nested object be flattened or treated as values
* @return {string} CSV string * @return {string} CSV string
*/ */
export function csv(target: any[], flatten=true) { export function toCsv(target: any[], flatten=true) {
const headers = target.reduce((acc, row) => { const headers = new ASet(target.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], []));
Object.keys(flatten ? flattenObj(row) : row)
.forEach(key => { if(!acc.includes(key)) acc.push(key); });
return acc;
}, []);
return [ return [
headers.join(','), headers.join(','),
...target.map(row => headers.map((h: string) => { ...target.map(row => headers.map((h: string) => {
const value = dotNotation<any>(row, h); const value = dotNotation<any>(row, h);
const type = typeof value; return (typeof value == 'object' && value != null) ? '"' + JSONSanitize(value).replaceAll('"', '""') + '"' : value;
if(type == 'string' && value.includes(',')) return `"${value}"`;
if(type == 'object') return `"${JSON.stringify(value)}"`;
return value;
}).join(',')) }).join(','))
].join('\n'); ].join('\n');
} }

View File

@ -52,6 +52,21 @@ export function fileBrowser(options: {accept?: string, multiple?: boolean} = {})
}); });
} }
/**
* Extract text from a file
*
* @param file File to extract text from
* @return {Promise<string | null>} File contents
*/
export function fileText(file: any): Promise<string | null> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(<string>reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsText(file);
});
}
/** /**
* Create timestamp intended for filenames from a date * Create timestamp intended for filenames from a date
* *

View File

@ -91,7 +91,6 @@ export function dotNotation<T>(obj: any, prop: string, set?: T): T | undefined {
}, obj); }, obj);
} }
/** /**
* Convert object into URL encoded query string * 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 = {}) { export function flattenObj(obj: any, parent?: any, result: any = {}) {
if(typeof obj === "object" && !Array.isArray(obj)) { if(typeof obj === "object" && !Array.isArray(obj)) {
for(const key of Object.keys(obj)) { for(const key of Object.keys(obj)) {
const propName = parent ? parent + '.' + key : key; const propName = parent ? `${parent}.${key}` : key;
if(typeof obj[key] === 'object') { if(typeof obj[key] === 'object' && obj[key] != null && !Array.isArray(obj[key])) {
console.log(propName, );
flattenObj(obj[key], propName, result); flattenObj(obj[key], propName, result);
} else { } else {
result[propName] = obj[key]; 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 * 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 * @return {string | T} Object if successful, original string otherwise
*/ */
export function JSONAttemptParse<T>(json: string): T | string { export function JSONAttemptParse<T1, T2>(json: T2): T1 | T2 {
try { return JSON.parse(json); } try { return JSON.parse(<any>json); }
catch { return json; } catch { return json; }
} }

View File

@ -1,22 +1,22 @@
/** /**
* String of all letters * String of all letters
*/ */
const LETTER_LIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; export const LETTER_LIST = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
/** /**
* String of all numbers * String of all numbers
*/ */
const NUMBER_LIST = '0123456789'; export const NUMBER_LIST = '0123456789';
/** /**
* String of all symbols * String of all symbols
*/ */
const SYMBOL_LIST = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/'; export const SYMBOL_LIST = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/';
/** /**
* String of all letters, numbers & symbols * String of all letters, numbers & symbols
*/ */
const CHAR_LIST = LETTER_LIST + NUMBER_LIST + SYMBOL_LIST; export const CHAR_LIST = LETTER_LIST + LETTER_LIST.toLowerCase() + NUMBER_LIST + SYMBOL_LIST;
/** /**
* Convert number of bytes into a human-readable size * Convert number of bytes into a human-readable size

View File

@ -84,36 +84,10 @@ export function formatDate(date: Date | number | string, format = 'YYYY-MM-DD H:
return (offset > 0 ? '-' : '') + `${hours}:${minutes.toString().padStart(2, '0')}`; return (offset > 0 ? '-' : '') + `${hours}:${minutes.toString().padStart(2, '0')}`;
} }
function timezone(offset: number) { function timezone(date: Date): string {
const hours = offset / 60; const formatter = new Intl.DateTimeFormat('en-US', {timeZoneName: 'short'});
switch (hours) { const formattedDate = formatter.format(date);
case -12: return "IDLW"; return formattedDate.split(' ').pop() || '';
case -11: return "SST";
case -10: return "HST";
case -9: return "AKST";
case -8: return "PST";
case -7: return "MST";
case -6: return "CST";
case -5: return "EST";
case -4: return "AST";
case -3: return "ART";
case -2: return "FNT";
case -1: return "AZOT";
case 0: return "UTC";
case 1: return "CET";
case 2: return "EET";
case 3: return "MSK";
case 4: return "SAMT";
case 5: return "YEKT";
case 6: return "OMST";
case 7: return "KRAT";
case 8: return "CST";
case 9: return "JST";
case 10: return "AEST";
case 11: return "SBT";
case 12: return "NZST";
default: return '';
}
} }
return format return format
@ -155,7 +129,7 @@ export function formatDate(date: Date | number | string, format = 'YYYY-MM-DD H:
// Timezone // Timezone
.replaceAll('ZZ', tzOffset(date.getTimezoneOffset()).replace(':', '')) .replaceAll('ZZ', tzOffset(date.getTimezoneOffset()).replace(':', ''))
.replaceAll('Z', tzOffset(date.getTimezoneOffset())) .replaceAll('Z', tzOffset(date.getTimezoneOffset()))
.replaceAll('z', timezone(date.getTimezoneOffset())); .replaceAll('z', timezone(date));
} }
/** /**