// src/CSVDoc.js
import fs from 'node:fs';
import { Aggregation } from './Aggregation.js';
/**
* This class is responsible for reading a CSV file,
* processing its content, and providing methods to access its data.
* It reads the file, processes the CSV content, and provides methods to get
* the file path, file content, column names, and perform aggregations on
* specific columns.
*/
export class CSVDoc {
// Instance variables
filePath;
file;
fileColumn;
fileData;
separator;
/**
* Setup the CSVDoc instance with the file path.
* methods to access its data. It reads the file, processes the CSV content,
* and provides methods to get the file path, file content, column names,
* and perform aggregations on specific columns.
* @param {string} filePath The absolute path to the csv file
* @param {string} [separator] The separator of the CSV
*/
constructor(filePath, separator = ',') {
this.#checkValid(filePath);
this.filePath = filePath;
this.file = fs.readFileSync(filePath);
this.separator = separator;
this.#processCSV();
}
/**
* Get the path of the file
* @returns {string} The string of the absolute file path
*/
getFilePath() {
return this.filePath;
}
/**
* Get the fs object of the file
* @returns {object} The fs.readFileSync() object of the file
*/
getFile() {
return this.file;
}
/**
* Get the column name of the csv that is imported
* @returns {Array} An array of column names
*/
getColumns() {
return this.fileColumn;
}
/**
* Get the aggregation data for the given column.
* @param {string} name The name of the column
* @returns {Aggregation} Returns the aggregation class for the column
*/
aggregateColumn(name) {
if (!name || typeof name !== 'string') {
throw new TypeError('Column name must be a non-empty string.');
}
if (!this.fileColumn.includes(name)) {
throw new Error(`Column "${name}" does not exist in the CSV file.`);
}
const index = this.fileColumn.indexOf(name);
const column = this.fileData.map((value) => value[index]);
return new Aggregation(column);
}
/**
* Process the csv
*/
#processCSV() {
let content = this.file.toString('utf-8').split('\n');
content = content.map((content) => content.split(this.separator));
this.fileColumn = content.shift();
this.fileData = content;
}
/**
* Checks if the filepath is valid and if the file exists at the given path.
* @param {string} filePath The absolute path to the file to check.
* @throws {TypeError} If filePath is not provided.
* @throws {Error} If the file does not exist at the provided path.
* @returns {boolean} True if the file is in the right format, otherwise throws an error.
*/
#checkValid(filePath) {
if (!filePath || '' == filePath || undefined == filePath) {
throw new TypeError('filePath must be provided.');
}
if (!fs.existsSync(filePath)) {
throw new Error('File does not exist at the provided path: ' + filePath);
}
if (!filePath.toUpperCase().endsWith('.CSV')) {
throw new TypeError('File is not a CSV file: ' + filePath);
}
return true;
}
}