mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-29 16:16:02 +00:00
feat: csv to json
This commit is contained in:
103
src/pages/tools/csv/csv-to-json/service.ts
Normal file
103
src/pages/tools/csv/csv-to-json/service.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
type CsvToJsonOptions = {
|
||||
delimiter: string;
|
||||
quote: string;
|
||||
comment: string;
|
||||
useHeaders: boolean;
|
||||
skipEmptyLines: boolean;
|
||||
dynamicTypes: boolean;
|
||||
};
|
||||
|
||||
const defaultOptions: CsvToJsonOptions = {
|
||||
delimiter: ',',
|
||||
quote: '"',
|
||||
comment: '#',
|
||||
useHeaders: true,
|
||||
skipEmptyLines: true,
|
||||
dynamicTypes: true
|
||||
};
|
||||
|
||||
export const convertCsvToJson = (
|
||||
csv: string,
|
||||
options: Partial<CsvToJsonOptions> = {}
|
||||
): string => {
|
||||
const opts = { ...defaultOptions, ...options };
|
||||
const lines = csv.split('\n');
|
||||
const result: any[] = [];
|
||||
let headers: string[] = [];
|
||||
|
||||
// Filter out comments and empty lines
|
||||
const validLines = lines.filter((line) => {
|
||||
const trimmedLine = line.trim();
|
||||
return (
|
||||
trimmedLine &&
|
||||
(!opts.skipEmptyLines ||
|
||||
!containsOnlyCustomCharAndSpaces(trimmedLine, opts.delimiter)) &&
|
||||
!trimmedLine.startsWith(opts.comment)
|
||||
);
|
||||
});
|
||||
|
||||
if (validLines.length === 0) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
// Parse headers if enabled
|
||||
if (opts.useHeaders) {
|
||||
headers = parseCsvLine(validLines[0], opts);
|
||||
validLines.shift();
|
||||
}
|
||||
|
||||
// Parse data lines
|
||||
for (const line of validLines) {
|
||||
const values = parseCsvLine(line, opts);
|
||||
|
||||
if (opts.useHeaders) {
|
||||
const obj: Record<string, any> = {};
|
||||
headers.forEach((header, i) => {
|
||||
obj[header] = parseValue(values[i], opts.dynamicTypes);
|
||||
});
|
||||
result.push(obj);
|
||||
} else {
|
||||
result.push(values.map((v) => parseValue(v, opts.dynamicTypes)));
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(result, null, 2);
|
||||
};
|
||||
|
||||
const parseCsvLine = (line: string, options: CsvToJsonOptions): string[] => {
|
||||
const values: string[] = [];
|
||||
let currentValue = '';
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
|
||||
if (char === options.quote) {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (char === options.delimiter && !inQuotes) {
|
||||
values.push(currentValue.trim());
|
||||
currentValue = '';
|
||||
} else {
|
||||
currentValue += char;
|
||||
}
|
||||
}
|
||||
|
||||
values.push(currentValue.trim());
|
||||
return values;
|
||||
};
|
||||
|
||||
const parseValue = (value: string, dynamicTypes: boolean): any => {
|
||||
if (!dynamicTypes) return value;
|
||||
|
||||
if (value.toLowerCase() === 'true') return true;
|
||||
if (value.toLowerCase() === 'false') return false;
|
||||
if (value === 'null') return null;
|
||||
if (!isNaN(Number(value))) return Number(value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
function containsOnlyCustomCharAndSpaces(str: string, customChar: string) {
|
||||
const regex = new RegExp(`^[${customChar}\\s]*$`);
|
||||
return regex.test(str);
|
||||
}
|
||||
Reference in New Issue
Block a user