diff options
| author | Priyansh <[email protected]> | 2022-01-19 13:19:26 -0500 |
|---|---|---|
| committer | Priyansh <[email protected]> | 2022-01-19 13:19:26 -0500 |
| commit | 6af5afc1b0d3b08b731bead0a4e2cad27dfd472c (patch) | |
| tree | 01adfbdfc7b57ce9b2cc8c85985782a6524bdc21 | |
| parent | 420085e4a3ab242523b462fd12a2d07c1693f2aa (diff) | |
| download | izuku.js-6af5afc1b0d3b08b731bead0a4e2cad27dfd472c.tar.xz izuku.js-6af5afc1b0d3b08b731bead0a4e2cad27dfd472c.zip | |
feat: info functions
| -rw-r--r-- | package-lock.json | 81 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | src/helpers/memorySize.js | 96 | ||||
| -rw-r--r-- | src/index.ts | 14 | ||||
| -rw-r--r-- | src/lib/display.ts | 7 | ||||
| -rw-r--r-- | src/lib/frame.ts | 54 | ||||
| -rw-r--r-- | src/lib/info.ts | 88 | ||||
| -rw-r--r-- | tests/info.test.ts | 25 | ||||
| -rw-r--r-- | tests/printing.test.ts | 14 | ||||
| -rw-r--r-- | tests/support/people.ts | 12 | ||||
| -rw-r--r-- | tsconfig.json | 2 |
11 files changed, 369 insertions, 28 deletions
diff --git a/package-lock.json b/package-lock.json index f8ecda6..ef9a881 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "buffer": "^6.0.3", "table": "^6.8.0" }, "devDependencies": { @@ -1121,6 +1122,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1180,6 +1200,29 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -2487,6 +2530,25 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -5684,6 +5746,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5731,6 +5798,15 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -6701,6 +6777,11 @@ "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", "dev": true }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", diff --git a/package.json b/package.json index 87552fe..30e0cfb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "start:dev": "nodemon", "build": "rm -rf dist && rimraf ./build && tsc", "lint": "eslint . --ext .ts", - "format": "prettier --config .prettierrc 'src/**/*.ts' --write" + "format": "prettier --config .prettierrc 'src/**/*.ts' --write", + "test-single": "func() { env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register \"$1\"; }; func" }, "repository": { "type": "git", @@ -58,6 +59,7 @@ ] }, "dependencies": { + "buffer": "^6.0.3", "table": "^6.8.0" } } diff --git a/src/helpers/memorySize.js b/src/helpers/memorySize.js new file mode 100644 index 0000000..044681a --- /dev/null +++ b/src/helpers/memorySize.js @@ -0,0 +1,96 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const Buffer = require('buffer/').Buffer; + +const ECMA_SIZES = { + STRING: 2, + BOOLEAN: 4, + NUMBER: 8 +}; + +function allProperties(obj) { + const stringProperties = []; + for (var prop in obj) { + stringProperties.push(prop); + } + if (Object.getOwnPropertySymbols) { + var symbolProperties = Object.getOwnPropertySymbols(obj); + Array.prototype.push.apply(stringProperties, symbolProperties); + } + return stringProperties; +} + +function sizeOfObject(seen, object) { + if (object == null) { + return 0; + } + + var bytes = 0; + var properties = allProperties(object); + for (var i = 0; i < properties.length; i++) { + var key = properties[i]; + // Do not recalculate circular references + if (typeof object[key] === 'object' && object[key] !== null) { + if (seen.has(object[key])) { + continue; + } + seen.add(object[key]); + } + + bytes += getCalculator(seen)(key); + try { + bytes += getCalculator(seen)(object[key]); + } catch (ex) { + if (ex instanceof RangeError) { + // circular reference detected, final result might be incorrect + // let's be nice and not throw an exception + bytes = 0; + } + } + } + + return bytes; +} + +function getCalculator(seen) { + return function calculator(object) { + if (Buffer.isBuffer(object)) { + return object.length; + } + + var objectType = typeof object; + switch (objectType) { + case 'string': + return object.length * ECMA_SIZES.STRING; + case 'boolean': + return ECMA_SIZES.BOOLEAN; + case 'number': + return ECMA_SIZES.NUMBER; + case 'symbol': + // eslint-disable-next-line no-case-declarations + const isGlobalSymbol = Symbol.keyFor && Symbol.keyFor(object); + return isGlobalSymbol + ? Symbol.keyFor(object).length * ECMA_SIZES.STRING + : (object.toString().length - 8) * ECMA_SIZES.STRING; + case 'object': + if (Array.isArray(object)) { + return object.map(getCalculator(seen)).reduce(function (acc, curr) { + return acc + curr; + }, 0); + } else { + return sizeOfObject(seen, object); + } + default: + return 0; + } + }; +} + +/** + * Main module's entry point + * Calculates Bytes for the provided parameter + * @param object - handles object/string/boolean/buffer + * @returns {*} + */ +export function sizeof(object) { + return getCalculator(new WeakSet())(object); +} diff --git a/src/index.ts b/src/index.ts index f4a56a2..d4be9aa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,21 @@ -import { data, header } from './lib/frame'; +import { data, generateHeader, setHeader, header } from './lib/frame'; import { getSingleColumnDetails } from './lib/locate'; import { show, head, tail } from './lib/display'; +import { getSize, info } from './lib/info'; class Izuku { rowdata: unknown[][] = []; - columns: string[] = []; + columns: string[]; + size = 0; + shape = '0 x 0'; constructor(rowdata?: Array<unknown[]>, columns?: Array<string>) { this.rowdata = rowdata || []; - this.columns = columns || []; + this.columns = columns + ? setHeader(this.rowdata, columns) + : generateHeader(this.rowdata); + this.size = getSize(this.rowdata); + this.shape = `${this.rowdata.length} x ${this.columns.length}`; } public data = data; @@ -16,6 +23,7 @@ class Izuku { public show = show; public head = head; public tail = tail; + public info = info; public column = (column: number | string) => { const izSampler = getSingleColumnDetails(this, column); return new Izuku(izSampler.rowd, izSampler.rowh); diff --git a/src/lib/display.ts b/src/lib/display.ts index 432ac82..c7a20c8 100644 --- a/src/lib/display.ts +++ b/src/lib/display.ts @@ -1,6 +1,13 @@ import Izuku from '../index'; import { table } from 'table'; +/** + * getTable returns the data compatible with table.table + * @param rowdata the rowdata to be sent to the frame + * @param columns the header columns to be sent to the frame + * @param indexRow index of the row to be printed + * @returns Array of arrays + */ function getTable( rowdata: unknown[][], columns: string[], diff --git a/src/lib/frame.ts b/src/lib/frame.ts index 1106ede..06ba4a9 100644 --- a/src/lib/frame.ts +++ b/src/lib/frame.ts @@ -1,4 +1,5 @@ import Izuku from '../index'; +import { getSize } from './info'; /** * data prints the data of the frame in console.table format. It also sets the new data to the frame if data is passed as a parameter * @param rowdata: the rowdata to be sent to the frame @@ -10,6 +11,7 @@ export function data( ): unknown[][] | any { if (rowdata) { this.rowdata = rowdata; + this.size = getSize(this.rowdata); } return this; @@ -24,19 +26,51 @@ export function header( this: Izuku, header: Array<string> ): Array<string> | any { - if (!this.rowdata.length) { - throw new Error('Set data before setting header'); + console.log(header); + if (!header.length) { + this.columns = generateHeader(this.rowdata); } else { - const passedHeaderLength = header.length; - const maxSizedArrayLength = this.rowdata.reduce((acc, curr) => { - return acc.length > curr.length ? acc : curr; - }).length; - if (passedHeaderLength !== maxSizedArrayLength) { - throw new Error('Header length does not match data length'); + this.columns = setHeader(this.rowdata, header); + } + + return this; +} + +/** + * setHeader sets the names of the columns of the frame + * @param rowdata: the rowdata to be sent to the frame + * @param header: the header to be attached to the frame + * @returns a new header + */ + +export function setHeader(rowdata: any[][], header: any[]): Array<string> { + const maxSizedArrayLength = rowdata.reduce((acc, curr) => { + return acc.length > curr.length ? acc : curr; + }).length; + const newHeaderArray = Array(maxSizedArrayLength).fill(''); + for (let i = 0; i < maxSizedArrayLength; i++) { + if (header[i]) { + newHeaderArray[i] = header[i]; } else { - this.columns = header; + newHeaderArray[i] = `Column ${i + 1}`; } } + return newHeaderArray; +} - return this; +/** + * generateHeader generates the names of the columns of the frame + * @param rowdata: the rowdata to be sent to the frame + * @returns a new header + */ + +export function generateHeader(rd: Array<any[]>): Array<string> { + const maxSizedArrayLength = rd.reduce((acc, curr) => { + return acc.length > curr.length ? acc : curr; + }).length; + const header: Array<string> = []; + for (let i = 0; i < maxSizedArrayLength; i++) { + header.push(`Column ${i + 1}`); + } + return header; } diff --git a/src/lib/info.ts b/src/lib/info.ts new file mode 100644 index 0000000..f7d3378 --- /dev/null +++ b/src/lib/info.ts @@ -0,0 +1,88 @@ +import Izuku from '../index'; +import { table } from 'table'; +import { sizeof } from '../helpers/memorySize'; +/** + * size returns the total number of elements in the frame + * @returns the total number of elements in the frame + * @returns 0 if the frame is empty + */ +export function getSize(rowdata: any[]): number { + // Get the number of elements in 2D array, do not count nulls + const numberOfElements = rowdata.reduce( + ( + acc: any, + row: { + filter: (arg0: (item: any) => boolean) => { + (): any; + new (): any; + length: any; + }; + } + ) => { + return acc + row.filter((item: null) => item !== null).length; + }, + 0 + ); + return numberOfElements; +} + +/** + * info returns the type of data present in each column of the frame + * @returns the type of data present in each column of the frame + */ +export function info(this: Izuku): void { + const info: Array<any[]> = []; + info.push(['#', 'Column Name', 'Types', 'Empty Values']); + let counter = 0; + const countDataTypes = {} as any; + this.columns.forEach((column: string, index: number) => { + // get all the types of data in the column, do not repeat the same type + let nullValuesPresentInRow = false; + const columnDataTypes = this.rowdata.map((row: any[]) => { + if ( + row[index] === null || + row[index] === undefined || + row[index] === '' + ) { + nullValuesPresentInRow = true; + } + const currentType = String(typeof row[index]); + if (!Object.keys(countDataTypes).includes(currentType)) { + Object.assign(countDataTypes, { [currentType]: 1 }); + } else { + const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => + obj[key]; + const currentValue = getKeyValue(countDataTypes, currentType); + Object.assign(countDataTypes, { [currentType]: currentValue + 1 }); + } + return typeof row[index]; + }); + const uniqueDataTypes = [...new Set(columnDataTypes)]; + info.push([counter, column, [...uniqueDataTypes], nullValuesPresentInRow]); + counter++; + }); + let dataTypesString = ''; + // Iterate through the countDataTypes object and create a string of the data types + Object.keys(countDataTypes).forEach((key: string) => { + dataTypesString += `${key}(${countDataTypes[key]})`; + // check if the key is not the last key in the object + if ( + Object.keys(countDataTypes).indexOf(key) !== + Object.keys(countDataTypes).length - 1 + ) { + dataTypesString += ', '; + } + }); + + console.log(`RangeIndex: ${this.size} elements, 0 to ${this.size - 1}`); + console.log( + `Shape: ${this.shape.split(' x ')[0].trim()} rows, ${this.shape + .split(' x ')[1] + .trim()} columns` + ); + console.log(table(info)); + // Remove the previous printed newline + process.stdout.write('\x1B[1A\x1B[2K'); + console.log(`Data Types: ${dataTypesString}`); + console.log(`Memory Usage: ${sizeof(this)} bytes`); +} diff --git a/tests/info.test.ts b/tests/info.test.ts new file mode 100644 index 0000000..c761e7f --- /dev/null +++ b/tests/info.test.ts @@ -0,0 +1,25 @@ +import Izuku from '../src/index'; +import { expect } from 'chai'; +import { data, header } from './support/people'; + +const frame = new Izuku(data, header); + +describe('info.ts', () => { + describe('Print size of frame', () => { + it('should print the size of the frame', () => { + const size = frame.size; + expect(size).to.equal(34); + }); + }); + describe('Print shape of frame', () => { + it('should print the shape of the frame', () => { + const shape = frame.shape; + expect(shape).to.equal('9 x 4'); + }); + }); + describe('Print type of data in each column', () => { + it('should print the type of data in each column', () => { + frame.info(); + }); + }); +}); diff --git a/tests/printing.test.ts b/tests/printing.test.ts index ab34490..49b53b2 100644 --- a/tests/printing.test.ts +++ b/tests/printing.test.ts @@ -1,17 +1,5 @@ import Izuku from '../src/index'; - -const header = ['Name', 'Age', 'Gender', 'Country']; -const data = [ - ['Arthur', 21, 'Male', 'USA'], - ['Betty', 20, 'Female', 'Canada'], - ['Victor', 25, 'Male'], - ['Dodger', 30, 'Male', 'Canada'], - ['Rayan', 21, 'Male', 'Russia'], - ['Skitley', 29, 'Female', 'Germany'], - ['Victoria', 89, 'Female', 'UK'], - ['Tiger', 23, 'Male', 'India'], - ['Killjoy', null, 'Female', 'Riot'] -]; +import { data, header } from './support/people'; const frame = new Izuku(data, header); diff --git a/tests/support/people.ts b/tests/support/people.ts new file mode 100644 index 0000000..c4978b2 --- /dev/null +++ b/tests/support/people.ts @@ -0,0 +1,12 @@ +export const header = ['Name', 'Age', 'Gender', 'Country']; +export const data = [ + ['Arthur', 21, 'Male', 'USA'], + ['Betty', 20, 'Female', 'Canada'], + ['Victor', 25, 'Male'], + ['Dodger', 30, 'Male', 'Canada'], + ['Rayan', 21, 'Male', 'Russia'], + ['Skitley', 29, 'Female', 'Germany'], + ['Victoria', 89, 'Female', 'UK'], + ['Tiger', 23, 'Male', 'India'], + ['Killjoy', null, 'Female', 'Riot'] +]; diff --git a/tsconfig.json b/tsconfig.json index 66353cc..7245288 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -52,7 +52,7 @@ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ |
