Untitled

mail@pastecode.io avatar
unknown
tsx
a year ago
9.2 kB
10
Indexable
Never
export default class Relation {
    private _name: string;
    private data: any[];
    private columnNames: string[];
  
    constructor(name: string, data: any[], columnNames?: string[]) {
        this._name = name;
        this.data = data;
        this.columnNames = columnNames || (data.length > 0 ? Object.keys(data[0]) : []);
    }
    
    public async sendToCsv(csvFilePath: string): Promise<void> {
      // Use a library like `csv-stringify` to write the DoubleEntryArray data to a CSV file
    }
  
    public getColumnNames(): string[] {
        return this.columnNames;
    }
  
    public changeColumnName(oldName: string, newName: string): Relation {
      const columnNames = this.getColumnNames();
      const index = columnNames.indexOf(oldName);
      if (index >= 0) {
        columnNames[index] = newName;
        this.data.forEach((row) => {
          row[newName] = row[oldName];
          delete row[oldName];
        });
      }
      return this;
    }

    public selectColumns(columnNames: string[]): Relation {
        this.data = this.data.map((row) => {
            const newRow: Record<string, any> = {};
            columnNames.forEach((columnName) => {
                newRow[columnName] = row[columnName];
            });
            return newRow;
        });
        this.columnNames = columnNames;
        return this;
    }

    public getData(): any[] {
        return this.data;
    }

    public get name(): string {
        return this._name;
    }
    private set name(name: string) {
        this._name = name;
    }

  
    public removeColumn(name: string): Relation {
        const index = this.getColumnNames().indexOf(name);
        if (index >= 0) {
            this.data.forEach((row) => {
            delete row[name];
            });
        }

        this.columnNames = this.columnNames.filter((columnName) => columnName !== name);
        return this;
    }
  
    public clone(): Relation {
      return new Relation(this.name, JSON.parse(JSON.stringify(this.data)), JSON.parse(JSON.stringify(this.columnNames)));
    }
  
    public removeRowsWithCheck( checkFn: (value:  Record<string, any>) => boolean): Relation {
        this.selectRowsWithCheck((value) => !checkFn(value));
        return this;
    }

    public selectRowsWithCheck(checkFn: (value: Record<string, any>) => boolean): Relation {
        this.data = this.data.filter((row) => checkFn(row));
        return this;
    }

  
    public print(): void {
      console.table(this.data);
    }

    public addRow(row: Record<string, any>): Relation {
        const hasDuplicate = this.data.some((r) => {
          return Object.keys(row).every((key) => r[key] === row[key]);
        });
    
        if (!hasDuplicate) {
          this.data.push(row);
        }
        return this;
    }

    public getName(): string {
        return this.name;
    }

    public setName(name: string): Relation {
        this.name = name;
        return this;
    }

    public equals(relation: Relation): boolean {
        if(this.name !== relation.name) return false;
        if(this.data.length !== relation.data.length) return false;
        if(this.columnNames.length !== relation.columnNames.length) return false;
        for(let i = 0; i < this.data.length; i++){
            if(JSON.stringify(this.data[i]) !== JSON.stringify(relation.data[i])) return false;
        }
        for(let i = 0; i < this.columnNames.length; i++){
            if(this.columnNames[i] !== relation.columnNames[i]) return false;
        }
        return true;
    }

    public renameColumns(oldNames: string[], newNames: string[]): Relation {
        oldNames.forEach((oldName, index) => {
            this.changeColumnName(oldName, newNames[index]);
        });
        return this;
    }

    public join(relation: Relation, checkFn: (row1: Record<string, any>, row2: Record<string, any>) => boolean) : Relation {
        // concat all column names with the relation name to avoid conflicts
        const columnNames = this.columnNames.map((columnName) => this.name + '.' + columnName);
        const relationColumnNames = relation.columnNames.map((columnName) => relation.name + '.' + columnName);
        columnNames.push(...relationColumnNames);
        
        const newdata: Record<string, any>[] = [];
        this.data.forEach((row1) => {
            relation.data.forEach((row2) => {
                if (checkFn(row1, row2)) {
                    const newRow: Record<string, any> = {};
                    columnNames.forEach((columnName) => {
                        const [relationName, name] = columnName.split('.');
                        newRow[columnName] = relationName === this.name ? row1[name] : row2[name];
                    });
                    newdata.push(newRow);
                }
            });
        });
        this.data = newdata;
        this.columnNames = columnNames;
        this.name = this.name + ' join ' + relation.name;
        return this;
    }

    public union(relation: Relation): Relation {
        // check if column names are the same and in the same order
        if (this.columnNames.length !== relation.columnNames.length) {
            throw new Error('Cannot union relations with different number of columns');
        }
        this.columnNames.forEach((columnName, index) => {
            if (columnName !== relation.columnNames[index]) {
                throw new Error('Cannot union relations with different column names');
            }
        });

        const newdata = this.data.concat(relation.data);
        // remove duplicates
        const newdataWithoutDuplicates = newdata.filter((row, index) => {
            return newdata.findIndex((r) => JSON.stringify(r) === JSON.stringify(row)) === index;
        });
        
        this.data = newdataWithoutDuplicates;
        this.columnNames = this.columnNames;
        this.name = this.name + ' union ' + relation.name;
        return this;
    }

    public intersection(relation: Relation): Relation {
        // check if column names are the same and in the same order
        if (this.columnNames.length !== relation.columnNames.length) {
            throw new Error('Cannot intersect relations with different number of columns');
        }
        this.columnNames.forEach((columnName, index) => {
            if (columnName !== relation.columnNames[index]) {
                throw new Error('Cannot intersect relations with different column names');
            }
        });

        const newdata: Record<string, any>[] = [];
        this.data.forEach((row1) => {
            relation.data.forEach((row2) => {
                if (JSON.stringify(row1) === JSON.stringify(row2)) {
                    newdata.push(row1);
                }
            });
        });
        this.data = newdata;
        this.columnNames = this.columnNames;
        this.name = this.name + ' intersect ' + relation.name;
        return this;
    }

    public difference(relation: Relation): Relation {
        // check if column names are the same and in the same order
        if (this.columnNames.length !== relation.columnNames.length) {
            throw new Error('Cannot difference relations with different number of columns');
        }
        this.columnNames.forEach((columnName, index) => {
            if (columnName !== relation.columnNames[index]) {
                throw new Error('Cannot difference relations with different column names');
            }
        });

        const newdata: Record<string, any>[] = [];
        this.data.forEach((row1) => {
            relation.data.forEach((row2) => {
                if (JSON.stringify(row1) !== JSON.stringify(row2)) {
                    newdata.push(row1);
                }
            });
        });
        this.data = newdata;
        this.columnNames = this.columnNames;
        this.name = this.name + ' difference ' + relation.name;
        return this;
    }

    public product(relation: Relation): Relation {
        // concat all column names with the relation name to avoid conflicts
        const newColumnNames = this.columnNames.map((thiscolumnName) => this.name + '.' + thiscolumnName);
        const relationColumnNames = relation.columnNames.map((relationCN) => relation.name + '.' + relationCN);
        newColumnNames.push(...relationColumnNames);
        const newdata: Record<string, any>[] = [];
        this.data.forEach((row1) => {
            relation.data.forEach((row2) => {
                const newRow: Record<string, any> = {};
                newColumnNames.forEach((newCN) => {
                    const [relationName, name] = newCN.split('.');
                    newRow[newCN] = relationName === this.name ? row1[name] : row2[name];
                });
                newdata.push(newRow);
            });
        });
        this.data = newdata;
        this.columnNames = newColumnNames;
        this.name = this.name + ' product ' + relation.name;
        return this;
    }
}