purify-objects
Version:
A powerful TypeScript library for cleaning objects by removing empty values, with support for YAML and CSV formats
135 lines (116 loc) • 4.32 kB
text/typescript
import { YAMLNode, CSVOptions, AnyObject } from './types';
export function parseYAML(content: string): AnyObject {
const lines = content.split('\n');
const root: AnyObject = {};
let currentNode: AnyObject = root;
let stack: [AnyObject, number][] = [[root, -1]];
for (const line of lines) {
if (!line.trim() || line.trim().startsWith('#')) continue;
const indent = line.search(/\S/);
const [key, ...valueParts] = line.trim().split(':');
let value = valueParts.join(':').trim();
while (stack.length > 1 && stack[stack.length - 1][1] >= indent) {
stack.pop();
}
currentNode = stack[stack.length - 1][0];
if (value) {
if (value.startsWith('[') && value.endsWith(']')) {
currentNode[key] = value.slice(1, -1).split(',').map(v => v.trim());
} else if (value.toLowerCase() === 'true') {
currentNode[key] = true;
} else if (value.toLowerCase() === 'false') {
currentNode[key] = false;
} else if (value === 'null') {
currentNode[key] = null;
} else if (!isNaN(Number(value))) {
currentNode[key] = Number(value);
} else {
currentNode[key] = value.replace(/^["']|["']$/g, '');
}
} else {
currentNode[key] = {};
stack.push([currentNode[key] as AnyObject, indent]);
}
}
return root;
}
export function parseCSV(content: string, options: CSVOptions): AnyObject[] {
const lines = content.split('\n').filter(line => line.trim());
const delimiter = options.delimiter || ',';
const result: AnyObject[] = [];
const headers = options.headers
? lines[0].split(delimiter).map(header => header.trim())
: Array.from({ length: lines[0].split(delimiter).length }, (_, i) => `column${i + 1}`);
const dataLines = options.headers ? lines.slice(1) : lines;
for (const line of dataLines) {
const values = line.split(delimiter).map(value => {
value = value.trim();
if (value.startsWith('"') && value.endsWith('"')) {
return value.slice(1, -1);
}
if (value === 'null') {
return null;
}
if (value === '') {
return value;
}
if (!isNaN(Number(value))) {
return Number(value);
}
if (value.toLowerCase() === 'true') {
return true;
}
if (value.toLowerCase() === 'false') {
return false;
}
return value;
});
const row: AnyObject = {};
headers.forEach((header, index) => {
row[header] = values[index];
});
result.push(row);
}
return result;
}
export function stringifyYAML(obj: AnyObject, indent = 0): string {
let result = '';
const spaces = ' '.repeat(indent);
for (const [key, value] of Object.entries(obj)) {
if (value === null || value === undefined) {
result += `${spaces}${key}:\n`;
} else if (value === '') {
result += `${spaces}${key}: ""\n`;
} else if (Array.isArray(value)) {
result += `${spaces}${key}: [${value.join(', ')}]\n`;
} else if (typeof value === 'object') {
result += `${spaces}${key}:\n${stringifyYAML(value as AnyObject, indent + 2)}`;
} else if (typeof value === 'string' && value.includes('\n')) {
result += `${spaces}${key}: |\n${value.split('\n').map(line => `${spaces} ${line}`).join('\n')}\n`;
} else {
result += `${spaces}${key}: ${value}\n`;
}
}
return result;
}
export function stringifyCSV(objects: AnyObject[], options: CSVOptions): string {
const delimiter = options.delimiter || ',';
const headers = options.headers
? Object.keys(objects[0])
: Array.from({ length: Object.keys(objects[0]).length }, (_, i) => `column${i + 1}`);
let result = options.headers ? headers.join(delimiter) + '\n' : '';
for (const obj of objects) {
const values = headers.map(header => {
const value = obj[header];
if (value === null || value === undefined) {
return '';
}
if (typeof value === 'string' && (value.includes(delimiter) || value.includes('"'))) {
return `"${value.replace(/"/g, '""')}"`;
}
return String(value);
});
result += values.join(delimiter) + '\n';
}
return result;
}