All files / src CSVDoc.js

100% Statements 98/98
100% Branches 22/22
100% Functions 8/8
100% Lines 98/98

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 1091x   1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x   4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x   4x 4x 4x 4x 4x 1x 1x   4x 4x 4x 4x 4x 1x 1x   4x 4x 4x 4x 4x 1x 1x   4x 4x 4x 4x 4x 4x 5x 2x 2x 5x 1x 1x   2x 2x 2x 5x   4x 4x 4x 4x 1x 1x 1x 1x 1x   4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 4x 1x 1x 4x 1x 1x 1x 4x 4x  
// 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;
  }
}