aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPriyansh <[email protected]>2022-01-19 13:19:26 -0500
committerPriyansh <[email protected]>2022-01-19 13:19:26 -0500
commit6af5afc1b0d3b08b731bead0a4e2cad27dfd472c (patch)
tree01adfbdfc7b57ce9b2cc8c85985782a6524bdc21
parent420085e4a3ab242523b462fd12a2d07c1693f2aa (diff)
downloadizuku.js-6af5afc1b0d3b08b731bead0a4e2cad27dfd472c.tar.xz
izuku.js-6af5afc1b0d3b08b731bead0a4e2cad27dfd472c.zip
feat: info functions
-rw-r--r--package-lock.json81
-rw-r--r--package.json4
-rw-r--r--src/helpers/memorySize.js96
-rw-r--r--src/index.ts14
-rw-r--r--src/lib/display.ts7
-rw-r--r--src/lib/frame.ts54
-rw-r--r--src/lib/info.ts88
-rw-r--r--tests/info.test.ts25
-rw-r--r--tests/printing.test.ts14
-rw-r--r--tests/support/people.ts12
-rw-r--r--tsconfig.json2
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. */