aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json4
-rw-r--r--src/helpers/arrayFunctions.ts43
-rw-r--r--src/index.ts7
-rw-r--r--src/lib/data.ts42
-rw-r--r--src/lib/display.ts6
-rw-r--r--src/lib/frame.ts18
-rw-r--r--src/lib/info.ts107
-rw-r--r--tests/data.test.ts44
-rw-r--r--tests/support/users.json34
-rw-r--r--tsconfig.json2
10 files changed, 242 insertions, 65 deletions
diff --git a/package.json b/package.json
index 30e0cfb..834ce1f 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,12 @@
"dist"
],
"scripts": {
- "test": "env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register 'tests/**/*.ts'",
+ "test": "env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\", \"resolveJsonModule\": true }' mocha -r ts-node/register 'tests/**/*.ts'",
"start:dev": "nodemon",
"build": "rm -rf dist && rimraf ./build && tsc",
"lint": "eslint . --ext .ts",
"format": "prettier --config .prettierrc 'src/**/*.ts' --write",
- "test-single": "func() { env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register \"$1\"; }; func"
+ "test-single": "func() { env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\", \"resolveJsonModule\": true }' mocha -r ts-node/register \"$1\"; }; func"
},
"repository": {
"type": "git",
diff --git a/src/helpers/arrayFunctions.ts b/src/helpers/arrayFunctions.ts
index 234e135..e6a6c50 100644
--- a/src/helpers/arrayFunctions.ts
+++ b/src/helpers/arrayFunctions.ts
@@ -54,3 +54,46 @@ export function range(
}
return rangeArray;
}
+
+/**
+ * flattenJSON - converts a nested JSON object into a simple JSON object
+ * @param object: the object to be flattened
+ * @returns the flattened object
+ */
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export function flattenJSON(data: any): any {
+ const result: any = {};
+ function recurse(cur: any, prop: string) {
+ if (Object(cur) !== cur) {
+ result[prop] = cur;
+ } else if (Array.isArray(cur)) {
+ // eslint-disable-next-line no-var
+ for (var i = 0, l = cur.length; i < l; i++)
+ recurse(cur[i], prop + '[' + i + ']');
+ if (l == 0) result[prop] = [];
+ } else {
+ let isEmpty = true;
+ for (const p in cur) {
+ isEmpty = false;
+ recurse(cur[p], prop ? prop + '.' + p : p);
+ }
+ if (isEmpty && prop) result[prop] = {};
+ }
+ }
+ recurse(data, '');
+ return result;
+}
+
+/**
+ * isValidJSONObject - checks if the object is a valid JSON object or a valid JSON string
+ * @param object: the object to be checked
+ * @returns true if the object is a valid JSON object or a valid JSON string
+ */
+export function isValidJSONObject(object: any): boolean {
+ try {
+ JSON.parse(JSON.stringify(object));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
diff --git a/src/index.ts b/src/index.ts
index acb613d..7d49ee5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,8 +8,8 @@ import {
} from './lib/locate';
import { show, head, tail } from './lib/display';
import { getSize, info } from './lib/info';
-import { flatten } from './lib/data';
-import { isArrayOfType, range } from './helpers/arrayFunctions';
+import { flatten, fromJSON } from './lib/data';
+import { isArrayOfType, range, flattenJSON } from './helpers/arrayFunctions';
class Izuku {
rowdata: unknown[][] = [];
@@ -61,6 +61,7 @@ class Izuku {
public rangeIndex = (index: number) => {
return rangeIndex(this, index);
};
+ public fromJSON = fromJSON;
}
class Frame extends Izuku {
@@ -69,4 +70,4 @@ class Frame extends Izuku {
}
}
-export { Frame, range };
+export { Frame, range, flattenJSON };
diff --git a/src/lib/data.ts b/src/lib/data.ts
index dcc948b..9649d6d 100644
--- a/src/lib/data.ts
+++ b/src/lib/data.ts
@@ -1,3 +1,6 @@
+import { Frame } from '../index';
+import { isValidJSONObject } from '../helpers/arrayFunctions';
+
/**
* flattenArray - flattens a 2D into a single array
* @param array: the array to be flattened
@@ -14,3 +17,42 @@ export function flatten(array: any[][]): any[] {
}
return flattenedArray;
}
+
+/**
+ * fromJSON - converts a JSON string into a rowdata - framedata is a 2D array
+ * @param json: the JSON string to be converted
+ * @returns the rowdata
+ */
+export function fromJSON(this: Frame, json: any): Frame {
+ if (isValidJSONObject(json)) {
+ const header: string[] = [];
+ const rowdata: any[][] = [];
+ // loop through each row
+ for (let i = 0; i < json.length; i++) {
+ // get all the keys and store them in the header
+ for (const key in json[i]) {
+ if (!header.includes(key)) {
+ header.push(key);
+ }
+ }
+ }
+ // loop through each row
+ for (let i = 0; i < json.length; i++) {
+ // get the values of the keys present in the header, if the values are not present in the row, add null
+ const row: any[] = [];
+ for (let j = 0; j < header.length; j++) {
+ if (json[i][header[j]] !== undefined) {
+ row.push(json[i][header[j]]);
+ } else {
+ row.push(null);
+ }
+ }
+ rowdata.push(row);
+ }
+ this.rowdata = rowdata;
+ this.columns = header;
+ return this;
+ } else {
+ throw new Error('Invalid JSON');
+ }
+}
diff --git a/src/lib/display.ts b/src/lib/display.ts
index 6aeb029..c33df70 100644
--- a/src/lib/display.ts
+++ b/src/lib/display.ts
@@ -40,7 +40,7 @@ function getTable(
* @throws Error if the frame is empty
*/
export function show(this: Frame): void {
- if (!this.rowdata.length) {
+ if (this.rowdata.length === 0) {
throw new Error('Set data before printing');
}
const numberOfRows = this.rowdata.length;
@@ -74,7 +74,7 @@ export function show(this: Frame): void {
* @throws Error if the frame is empty
*/
export function head(this: Frame, n = 5): void {
- if (!this.rowdata.length) {
+ if (this.rowdata.length === 0) {
throw new Error('Set data before printing');
}
// Check if n is greater than the number of rows
@@ -95,7 +95,7 @@ export function head(this: Frame, n = 5): void {
* @throws Error if the frame is empty
*/
export function tail(this: Frame, n = 5): void {
- if (!this.rowdata.length) {
+ if (this.rowdata.length === 0) {
throw new Error('Set data before printing');
}
// Check if n is greater than the number of rows
diff --git a/src/lib/frame.ts b/src/lib/frame.ts
index 8ed1316..d257fb4 100644
--- a/src/lib/frame.ts
+++ b/src/lib/frame.ts
@@ -63,12 +63,16 @@ export function setHeader(rowdata: any[][], header: any[]): Array<string> {
*/
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}`);
+ if (rd?.length) {
+ 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;
+ } else {
+ return [];
}
- return header;
}
diff --git a/src/lib/info.ts b/src/lib/info.ts
index 814992b..9338c75 100644
--- a/src/lib/info.ts
+++ b/src/lib/info.ts
@@ -17,58 +17,67 @@ export function getSize(rowdata: any[]): number {
* @returns the type of data present in each column of the frame
*/
export function info(this: Frame): 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 (this.rowdata.length === 0) {
+ throw new Error('Frame is empty');
+ } else {
+ 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 (
- row[index] === null ||
- row[index] === undefined ||
- row[index] === ''
+ Object.keys(countDataTypes).indexOf(key) !==
+ Object.keys(countDataTypes).length - 1
) {
- 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 });
+ dataTypesString += ', ';
}
- 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`);
+ 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/data.test.ts b/tests/data.test.ts
new file mode 100644
index 0000000..33eb56f
--- /dev/null
+++ b/tests/data.test.ts
@@ -0,0 +1,44 @@
+import { Frame } from '../src/index';
+import { expect } from 'chai';
+import JSONData from './support/users.json';
+
+describe('data.ts', () => {
+ describe('load frame from json file', () => {
+ it('should load the frame from json file', () => {
+ const dataToExpect = [
+ [
+ 1,
+ 'Jeanette',
+ 'Penddreth',
+ 'Female',
+ '26.58.193.2'
+ ],
+ [
+ 2,
+ 'Giavani',
+ 'Frediani',
+ 'Male',
+ '229.179.4.212'
+ ],
+ [3, 'Noell', 'Bea', '[email protected]', 'Female', '180.66.162.255'],
+ [4, 'Willard', 'Valek', '[email protected]', 'Male', '67.76.188.26']
+ ];
+ const headerToExpect = [
+ 'id',
+ 'first_name',
+ 'last_name',
+ 'email',
+ 'gender',
+ 'ip_address'
+ ];
+ expect(new Frame().fromJSON(JSONData).columns).to.deep.equal(
+ headerToExpect
+ );
+ expect(new Frame().fromJSON(JSONData).rowdata).to.deep.equal(
+ dataToExpect
+ );
+ });
+ });
+});
diff --git a/tests/support/users.json b/tests/support/users.json
new file mode 100644
index 0000000..628e8f5
--- /dev/null
+++ b/tests/support/users.json
@@ -0,0 +1,34 @@
+[
+ {
+ "id": 1,
+ "first_name": "Jeanette",
+ "last_name": "Penddreth",
+ "email": "[email protected]",
+ "gender": "Female",
+ "ip_address": "26.58.193.2"
+ },
+ {
+ "id": 2,
+ "first_name": "Giavani",
+ "last_name": "Frediani",
+ "email": "[email protected]",
+ "gender": "Male",
+ "ip_address": "229.179.4.212"
+ },
+ {
+ "id": 3,
+ "first_name": "Noell",
+ "last_name": "Bea",
+ "email": "[email protected]",
+ "gender": "Female",
+ "ip_address": "180.66.162.255"
+ },
+ {
+ "id": 4,
+ "first_name": "Willard",
+ "last_name": "Valek",
+ "email": "[email protected]",
+ "gender": "Male",
+ "ip_address": "67.76.188.26"
+ }
+]
diff --git a/tsconfig.json b/tsconfig.json
index 7245288..f381999 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -33,7 +33,7 @@
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
- // "resolveJsonModule": true, /* Enable importing .json files */
+ "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */