diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 0000000..214a228 --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,15 @@ +{ + "name": "challenge2019", + "lockfileVersion": 2, + "requires": true, + "packages": { + "node_modules/csv-reader": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/csv-reader/-/csv-reader-1.0.10.tgz", + "integrity": "sha512-4IgSQSwu/dCRjnrz+BnNe5LZiAHa2sOhccROI0r1gB1FW38Fl/k7Zx0jN3o77e4PTgd9T0cp1mgsD1esJNK+tQ==", + "engines": { + "node": ">=8.0.0" + } + } + } +} diff --git a/node_modules/csv-reader/LICENSE b/node_modules/csv-reader/LICENSE new file mode 100644 index 0000000..129c671 --- /dev/null +++ b/node_modules/csv-reader/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Daniel Cohen Gindi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/csv-reader/README.md b/node_modules/csv-reader/README.md new file mode 100644 index 0000000..339bb2d --- /dev/null +++ b/node_modules/csv-reader/README.md @@ -0,0 +1,125 @@ +# csv-reader + +[![npm Version](https://badge.fury.io/js/csv-reader.png)](https://npmjs.org/package/csv-reader) + +A CSV stream reader, with many many features, and ability to work with the largest datasets + +## Included features: (can be turned on and off) + +* Support for excel-style multiline cells wrapped in quotes +* Choosing a different delimiter instead of the comma +* Automatic skipping empty lines +* Automatic skipping of the first header row +* Automatic parsing of numbers and booleans +* Automatic trimming +* Being a stream transformer, you can `.pause()` if you need some time to process the row and `.resume()` when you are ready to receive and process more rows. +* Consumes and emits rows one-by-one, allowing you to process datasets in any size imaginable. +* Automatically strips the BOM if exists (not handled automatically by node.js stream readers) + +## Installation: + +``` +npm install --save csv-reader +``` + +The options you can pass are: + +Name | Type | Default | Explanation +---- | ---- | ------- | ----------- + `delimiter` | `String` | `,` | The character that separates between cells + `multiline` | `Boolean` | `true` | Allow multiline cells, when the cell is wrapped with quotes ("...\n...") + `allowQuotes` | `Boolean` | `true` | Should quotes be treated as a special character that wraps cells etc. + `skipEmptyLines` | `Boolean` | `false` | Should empty lines be automatically skipped? + `skipHeader` | `Boolean` | `false` | Should the first header row be skipped? + `asObject` | `Boolean` | `false` | If true, each row will be converted automatically to an object based on the header. This implied `skipHeader=true`. + `parseNumbers` | `Boolean` | `false` | Should numbers be automatically parsed? This will parse any format supported by `parseFloat` including scientific notation, `Infinity` and `NaN`. + `parseBooleans` | `Boolean` | `false` | Automatically parse booleans (strictly lowercase `true` and `false`) + `ltrim` | `Boolean` | `false` | Automatically left-trims columns + `rtrim` | `Boolean` | `false` | Automatically right-trims columns + `trim` | `Boolean` | `false` | If true, then both 'ltrim' and 'rtrim' are set to true + +## Events: + +A `'data'` event will be emitted with each row, either in an array format (`(string|number|boolean)[]`) or an Object format (`Object`), depending on the `asObject` option. +A preliminary `'header'` event will be emitted with the first row, only in an array format, and without any interpolation to different types (`string[]`). +Of course other events as usual - `end` and `error`. + +## Usage example: + +```javascript + +const Fs = require('fs'); +const CsvReadableStream = require('csv-reader'); + +let inputStream = Fs.createReadStream('my_data.csv', 'utf8'); + +inputStream + .pipe(new CsvReadableStream({ parseNumbers: true, parseBooleans: true, trim: true })) + .on('data', function (row) { + console.log('A row arrived: ', row); + }) + .on('end', function () { + console.log('No more rows!'); + }); + +``` + +A common issue with CSVs are that Microsoft Excel for some reason *does not save UTF8 files*. Microsoft never liked standards. +In order to automagically handle the possibility of such files with ANSI encodings arriving from user input, you can use the [autodetect-decoder-stream](https://www.npmjs.com/package/autodetect-decoder-stream) like this: + +```javascript + +const Fs = require('fs'); +const CsvReadableStream = require('csv-reader'); +const AutoDetectDecoderStream = require('autodetect-decoder-stream'); + +let inputStream = Fs.createReadStream('my_data.csv') + .pipe(new AutoDetectDecoderStream({ defaultEncoding: '1255' })); // If failed to guess encoding, default to 1255 + +// The AutoDetectDecoderStream will know if the stream is UTF8, windows-1255, windows-1252 etc. +// It will pass a properly decoded data to the CsvReader. + +inputStream + .pipe(new CsvReadableStream({ parseNumbers: true, parseBooleans: true, trim: true })) + .on('data', function (row) { + console.log('A row arrived: ', row); + }).on('end', function () { + console.log('No more rows!'); + }); + +``` + +## Contributing + +If you have anything to contribute, or functionality that you lack - you are more than welcome to participate in this! +If anyone wishes to contribute unit tests - that also would be great :-) + +## Me +* Hi! I am Daniel Cohen Gindi. Or in short- Daniel. +* danielgindi@gmail.com is my email address. +* That's all you need to know. + +## Help + +If you want to buy me a beer, you are very welcome to +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G6CELS3E997ZE) + Thanks :-) + +## License + +All the code here is under MIT license. Which means you could do virtually anything with the code. +I will appreciate it very much if you keep an attribution where appropriate. + + The MIT License (MIT) + + Copyright (c) 2013 Daniel Cohen Gindi (danielgindi@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. diff --git a/node_modules/csv-reader/index.d.ts b/node_modules/csv-reader/index.d.ts new file mode 100644 index 0000000..fd9a0af --- /dev/null +++ b/node_modules/csv-reader/index.d.ts @@ -0,0 +1,177 @@ +/// + +import { Transform, Readable } from "stream"; + +export declare type Options = { + /** + * Specify what is the CSV delimiter + * @default "," + */ + delimiter?: string; + + /** + * Support Excel-like multiline CSV + * @default true + */ + multiline?: boolean; + + /** + * Allow quotation marks to wrap columns + * @default true + */ + allowQuotes?: boolean; + + /** + * Should empty lines be automatically skipped? + * @default false + */ + skipEmptyLines?: boolean; + + /** + * Automatically parse numbers (with a . as the decimal separator) + * @default false + */ + parseNumbers?: boolean; + + /** + * Automatically parse booleans (strictly lowercase `true` and `false`) + * @default false + */ + parseBooleans?: boolean; + + /** + * Automatically left-trims columns + * @default false + */ + ltrim?: boolean; + + /** + * Automatically right-trims columns + * @default false + */ + rtrim?: boolean; + + /** + * If true, then both 'ltrim' and 'rtrim' are set to true + * @default false + */ + trim?: boolean; + + /** + * If true, then skip the first header row + * @default false + */ + skipHeader?: boolean; + + /** + * If true, each row will be converted automatically to an object based on the header. + * This implied `skipHeader=true`. + * @default false + */ + asObject?: boolean; +}; + +export declare type DataTypes = string | number | boolean; + +export declare type Line = DataTypes[] | { [header: string]: DataTypes }; + +declare interface CsvReadableStream extends Transform { + /** + * Create a new readable stream that parses CSV data into events, line by line + * @constructor + */ + new(options?: Options): this; + + /** + * Create a new readable stream that parses CSV data into events, line by line + */ + (options?: Options): this; + + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (line: Line) => void): this; + addListener(event: "header", listener: (headers: string[]) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "pipe", listener: (src: Readable) => void): this; + addListener(event: "unpipe", listener: (src: Readable) => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + + on(event: "close", listener: () => void): this; + on(event: "data", listener: (line: Line) => void): this; + on(event: "header", listener: (headers: string[]) => void): this; + on(event: "end", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "pipe", listener: (src: Readable) => void): this; + on(event: "unpipe", listener: (src: Readable) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + + once(event: "close", listener: () => void): this; + once(event: "data", listener: (line: Line) => void): this; + once(event: "header", listener: (headers: string[]) => void): this; + once(event: "end", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "pipe", listener: (src: Readable) => void): this; + once(event: "unpipe", listener: (src: Readable) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (line: Line) => void): this; + prependListener(event: "header", listener: (headers: string[]) => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "pipe", listener: (src: Readable) => void): this; + prependListener(event: "unpipe", listener: (src: Readable) => void): this; + prependListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this; + + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (line: Line) => void): this; + prependOnceListener( + event: "header", + listener: (headers: string[]) => void + ): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "pipe", listener: (src: Readable) => void): this; + prependOnceListener(event: "unpipe", listener: (src: Readable) => void): this; + prependOnceListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this; + + removeListener(event: "close", listener: () => void): this; + removeListener(event: "data", listener: (line: Line) => void): this; + removeListener(event: "header", listener: (headers: string[]) => void): this; + removeListener(event: "end", listener: () => void): this; + removeListener(event: "readable", listener: () => void): this; + removeListener(event: "drain", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "finish", listener: () => void): this; + removeListener(event: "pipe", listener: (src: Readable) => void): this; + removeListener(event: "unpipe", listener: (src: Readable) => void): this; + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this; +} + +declare const CsvReadableStream: CsvReadableStream; + +export default CsvReadableStream; diff --git a/node_modules/csv-reader/index.js b/node_modules/csv-reader/index.js new file mode 100644 index 0000000..c02b8de --- /dev/null +++ b/node_modules/csv-reader/index.js @@ -0,0 +1,300 @@ +const Stream = require('stream'); +const Util = require('util'); + +/** + * @const + * @type {RegExp} + */ +const PARSE_FLOAT_TEST = /^[-+]?\d+(?:\.\d*)?(?:[eE]\+\d+)?$|^(?:\d+)?\.\d+(?:e+\d+)?$|^[-+]?Infinity$|^[-+]?NaN$/; + +const Transform = Stream.Transform; + +/** + * @param {Object?} options + * @param {string} [options.delimiter=','] - Specify what is the CSV delimeter + * @param {boolean} [options.multiline=true] - Support Excel-like multiline CSV + * @param {boolean} [options.allowQuotes=true] - Allow quotation marks to wrap columns + * @param {boolean} [options.skipEmptyLines=false] - Should empty lines be automatically skipped? + * @param {boolean} [options.parseNumbers=false] - Automatically parse numbers (with a . as the decimal separator) + * @param {boolean} [options.parseBooleans=false] - Automatically parse booleans (strictly lowercase `true` and `false`) + * @param {boolean} [options.ltrim=false] - Automatically left-trims columns + * @param {boolean} [options.rtrim=false] - Automatically right-trims columns + * @param {boolean} [options.trim=false] - If true, then both 'ltrim' and 'rtrim' are set to true + * @param {boolean} [options.skipHeader=false] - If true, then skip the first header row + * @param {boolean} [options.asObject=false] - If true, each row will be converted automatically to an object based + * on the header. This implied `skipHeader=true`. + * @returns {CsvReadableStream} + * @constructor + */ +const CsvReadableStream = function (options) { + options = options || {}; + + //noinspection JSUndefinedPropertyAssignment + options.objectMode = true; + + if (!(this instanceof CsvReadableStream)) { + return new CsvReadableStream(options); + } + + let data = null, + dataIndex = null, + nextIndex = null, + dataLen = null, + column = '', + columnCount = 0, + lastLineEndCR = false, + lookForBOM = true, + isQuoted = false, + rowCount = 0; + + const multiline = !!options.multiline || typeof options.multiline === 'undefined', + delimiter = options.delimiter != null ? options.delimiter.toString() || ',' : ',', + allowQuotes = !!options.allowQuotes || typeof options.allowQuotes === 'undefined', + skipEmptyLines = !!options.skipEmptyLines, + parseNumbers = !!options.parseNumbers, + parseBooleans = !!options.parseBooleans, + ltrim = !!options.ltrim || !!options.trim, + rtrim = !!options.rtrim || !!options.trim, + trim = ltrim && rtrim, + asObject = !!options.asObject, + skipHeader = !!options.skipHeader || asObject, + + postProcessingEnabled = parseNumbers || parseBooleans || ltrim || rtrim; + + let headerRow = []; + + /** @type {*[]|Object} */ + let columns = asObject === true ? {} : []; + + const postProcessColumn = function (column) { + + if (trim) { + column = column.trim(); + } else if (ltrim) { + column = column.replace(/^\s+/, ''); + } else if (rtrim) { + column = column.replace(/\s+$/, ''); + } + + if (parseBooleans) { + if (column === 'true') { + return true; + } + if (column === 'false') { + return false; + } + } + + if (parseNumbers) { + if (PARSE_FLOAT_TEST.test(column)) { + return parseFloat(column); + } + } + + return column; + }; + + this._processChunk = function (newData) { + + if (newData) { + if (data) { + data = data.substring(dataIndex) + newData; + } else { + data = newData; + } + dataLen = data.length; + dataIndex = 0; + + // Node doesn't strip BOMs, that's in user's land + if (lookForBOM) { + if (newData.charCodeAt(0) === 0xfeff) { + dataIndex++; + } + lookForBOM = false; + } + } + + let isFinishedLine = false; + + const rowIndex = rowCount; + + for (; dataIndex < dataLen; dataIndex++) { + const c = data[dataIndex]; + + if (c === '\n' || c === '\r') { + if (!isQuoted || !multiline) { + if (lastLineEndCR && c === '\n') { + lastLineEndCR = false; + continue; + } + lastLineEndCR = c === '\r'; + dataIndex++; + isFinishedLine = true; + rowCount++; + + if (!multiline) { + isQuoted = false; + } + + break; + } + } + + if (isQuoted) { + if (c === '"') { + nextIndex = dataIndex + 1; + + // Do we have enough data to peek at the next character? + if (nextIndex >= dataLen && !this._isStreamDone) { + // Wait for more data to arrive + break; + } + + if (nextIndex < dataLen && data[nextIndex] === '"') { + column += '"'; + dataIndex++; + } else { + isQuoted = false; + } + } else { + column += c; + } + } else { + if (c === delimiter) { + if (rowIndex === 0) { + headerRow.push(column.trim()); + } + + if (column.length > 0 && postProcessingEnabled === true) { + column = postProcessColumn(column); + } + + if (asObject === true) { + columns[headerRow[columnCount]] = column; + } else { + columns.push(column); + } + + column = ''; + columnCount++; + } else if (c === '"' && allowQuotes) { + if (column.length) { + column += c; + } else { + isQuoted = true; + } + } else { + column += c; + } + } + } + + if (dataIndex === dataLen) { + data = null; + } + + if (isFinishedLine || (data === null && this._isStreamDone === true)) { + + if (columnCount > 0 || + column.length > 0 || + data !== null || + !this._isStreamDone) { + + const isEmptyRow = columnCount === 1 && column.length === 0; + + // Process last column + if (rowIndex === 0) { + headerRow.push(column.trim()); + this.emit('header', headerRow); + } + + if (column.length > 0 && postProcessingEnabled === true) { + column = postProcessColumn(column); + } + + if (asObject === true) { + columns[headerRow[columnCount]] = column; + } else { + columns.push(column); + } + + // Commit this row + let row = columns; + + // Clear row state data + columns = asObject === true ? {} : []; + column = ''; + columnCount = 0; + isQuoted = false; + + if (skipHeader === false || rowIndex > 0) { + // Is this row full or empty? + if (isEmptyRow === false || skipEmptyLines === false) { + // Emit the parsed row + //noinspection JSUnresolvedFunction + this.push(row); + } + } + + // Look to see if there are more rows in available data + this._processChunk(); + + } else { + // We just ran into a newline at the end of the file, ignore it + } + + } else { + + if (data) { + + // Let more data come in. + // We are probably waiting for a "peek" at the next character + + } else { + + // We have probably hit end of file. + // Let the end event come in. + + } + + } + + }; + + Transform.call(this, options); +}; + +Util.inherits(CsvReadableStream, Transform); + +//noinspection JSUnusedGlobalSymbols +CsvReadableStream.prototype._transform = function (chunk, enc, cb) { + + try { + this._processChunk(chunk); + + cb(); + } catch (err) { + cb(err); + } +}; + +//noinspection JSUnusedGlobalSymbols +CsvReadableStream.prototype._flush = function (cb) { + + try { + this._isStreamDone = true; + + this._processChunk(); + + cb(); + } catch (err) { + cb(err); + } + +}; + +/** + * @module + * @type {CsvReadableStream} + */ +module.exports = CsvReadableStream; diff --git a/node_modules/csv-reader/package.json b/node_modules/csv-reader/package.json new file mode 100644 index 0000000..db8c946 --- /dev/null +++ b/node_modules/csv-reader/package.json @@ -0,0 +1,43 @@ +{ + "name": "csv-reader", + "version": "1.0.10", + "description": "A CSV stream reader, with many many features, and ability to work with the largest datasets", + "main": "index.js", + "types": "index.d.ts", + "scripts": { + "lint": "eslint -f codeframe ./", + "lint-fix": "eslint -f codeframe --fix ./", + "test": "npm run lint && mocha ./tests/**/*test.js", + "prepublishOnly": "pinst --disable", + "postpublish": "pinst --enable" + }, + "repository": { + "type": "git", + "url": "https://github.com/danielgindi/node-csv-reader" + }, + "engines": { + "node": ">=8.0.0" + }, + "keywords": [ + "node.js", + "csv", + "reader", + "parser", + "stream", + "fast" + ], + "author": "Daniel Cohen Gindi (https://github.com/danielgindi)", + "license": "MIT", + "bugs": { + "url": "https://github.com/danielgindi/node-csv-reader/issues" + }, + "homepage": "https://github.com/danielgindi/node-csv-reader", + "dependencies": {}, + "devDependencies": { + "eslint": "^8.25.0", + "eslint-formatter-codeframe": "^7.32.1", + "husky": "^8.0.1", + "mocha": "^10.0.0", + "pinst": "^3.0.0" + } +} diff --git a/output1.csv b/output1.csv index 7c7b275..99ae731 100644 --- a/output1.csv +++ b/output1.csv @@ -1,4 +1,4 @@ -D1,true ,P1,2000 -D2,true ,P1,3250 -D3,true ,P3,15300 +D1,true,P1,2000 +D2,true,P1,3250 +D3,true,P3,15300 D4,false,"","" diff --git a/output2.csv b/output2.csv index adcd15b..7b53db2 100644 --- a/output2.csv +++ b/output2.csv @@ -1,4 +1,4 @@ -D1,true ,P2,3000 -D2,true ,P1,3250 -D3,true ,P3,15300 +D1,true,P2,3000 +D2,true,P1,3250 +D3,true,P3,15300 D4,false,"","" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..23a02cd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "challenge2019", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "csv-reader": "^1.0.10" + } + }, + "node_modules/csv-reader": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/csv-reader/-/csv-reader-1.0.10.tgz", + "integrity": "sha512-4IgSQSwu/dCRjnrz+BnNe5LZiAHa2sOhccROI0r1gB1FW38Fl/k7Zx0jN3o77e4PTgd9T0cp1mgsD1esJNK+tQ==", + "engines": { + "node": ">=8.0.0" + } + } + }, + "dependencies": { + "csv-reader": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/csv-reader/-/csv-reader-1.0.10.tgz", + "integrity": "sha512-4IgSQSwu/dCRjnrz+BnNe5LZiAHa2sOhccROI0r1gB1FW38Fl/k7Zx0jN3o77e4PTgd9T0cp1mgsD1esJNK+tQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0875145 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "dependencies": { + "csv-reader": "^1.0.10" + }, + "name": "challenge2019", + "description": "Qube delivers the movie content to theatres all around the world. There are multiple delivery partners to help us deliver the content.", + "version": "1.0.0", + "main": "index.js", + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Poornachandar30/challenge2019.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Poornachandar30/challenge2019/issues" + }, + "homepage": "https://github.com/Poornachandar30/challenge2019#readme" +} diff --git a/parser.js b/parser.js new file mode 100644 index 0000000..084f105 --- /dev/null +++ b/parser.js @@ -0,0 +1,87 @@ +const fs = require('fs') +const CsvReadableStream = require('csv-reader') +let input = []; + +// parsing partner csv and destructuring it +let partners = []; +function parsePartners() { + return new Promise((resolve, reject) => { + let partnerStream = fs.createReadStream('partners.csv', 'utf8'); + partnerStream + .pipe(new CsvReadableStream({ skipHeader: true, parseNumbers: true, trim: true })) + .on('data', function (row) { + try { + const sizes = row[1].split('-'); + partners.push({ + 'theatre': row[0], + 'minCost': row[2], + 'costPerGB': row[3], + 'partnerId': row[4], + 'minSizeSlab': +sizes[0], + 'maxSizeSlab': +sizes[1] + }) + console.log("partners", partners); + } + catch (err) { + console.error("Error while parsing : " + err) + reject(err) + } + }) + .on('end', function () { + resolve(partners) + }); + }) +} + +// parsing input csv and destructuring it +function parseInputs() { + return new Promise((resolve, reject) => { + let inputStream = fs.createReadStream('input.csv', 'utf8'); + inputStream + .pipe(new CsvReadableStream({ parseNumbers: true, trim: true })) + .on('data', function (row) { + try { + input.push({ + 'id': row[0], + 'size': row[1], + 'theatre': row[2], + }) + } + catch (err) { + console.error("Error while parsing : " + err) + reject(err) + } + }) + .on('end', function () { + resolve(input); + }); + }) +} + +// parsing capacity csv and destructuring it +let capacity = {}; +function parseCapacity() { + return new Promise((resolve, reject) => { + let inputStream = fs.createReadStream('capacities.csv', 'utf8'); + inputStream + .pipe(new CsvReadableStream({ skipHeader: true, parseNumbers: true, trim: true, })) + .on('data', function (row) { + try { + capacity[row[0]]= row[1]; + } + catch (err) { + console.error("Error while parsing : " + err) + reject(err) + } + }) + .on('end', function () { + resolve(capacity) + }); + }) + } + +module.exports = { + parsePartners, + parseInputs, + parseCapacity +} \ No newline at end of file diff --git a/solution1.js b/solution1.js new file mode 100644 index 0000000..95b73bd --- /dev/null +++ b/solution1.js @@ -0,0 +1,57 @@ +const fs = require('fs') +const { parseInputs, parsePartners} = require('./parser'); + +//To get the minimum cost of delivery +solution1(); + +//solution1() - Inside this function the solution for problem statement 1 is writtened here + async function solution1() { + const partners = await parsePartners(); + const input = await parseInputs(); + let result ; + //clearing output1.csv file + fs.writeFile("output1.csv", '', (err) => { + if (err) throw err; + }); +//Iterating the deivery input + for(let iterateInput = 0; iterateInput= input[iterateInput].size ){ + + usageCost = input[iterateInput].size * partners[iteratePartners].costPerGB; + //appending the minimum usage cost + usageCost = usageCost usageCost ? partners[iteratePartners].partnerId : partnerId; + minimumCost = minimumCost > usageCost ? usageCost : minimumCost; + } + else{ + minimumCost = usageCost; + partnerId = partners[iteratePartners].partnerId; + }} + } + partnerId = partnerId == undefined ?'""':partnerId; + minimumCost = minimumCost == undefined ?'""':minimumCost; + result = input[iterateInput].id +","+deliverable+","+ partnerId +","+minimumCost; + console.log("Final result",result); + //appending the result in output1.csv for every iteration + fs.appendFileSync("output1.csv", result + '\n'); + + } +} + +module.exports = { + solution1 +} \ No newline at end of file diff --git a/solution2.js b/solution2.js new file mode 100644 index 0000000..8bcfbc9 --- /dev/null +++ b/solution2.js @@ -0,0 +1,70 @@ +const fs = require('fs') +const { parseInputs, parsePartners, parseCapacity} = require('./parser'); + +//To get the minimum cost of total delivery +solution2(); + +async function solution2() { + const partners = await parsePartners(); + const input = await parseInputs(); + let result ; + let sortedResult=[]; + //clearing output2.csv file + fs.writeFile("output2.csv", '', (err) => { + if (err) throw err; + }); + + //sorting the input based on content size in Descending order + input.sort(function(a, b){return b.size - a.size}); + const capacity = await parseCapacity(); + //Iterating the deivery input + for(let iterateInput = 0; iterateInput= input[iterateInput].size + && capacity[partners[iteratePartners].partnerId]>=input[iterateInput].size){ + + usageCost = input[iterateInput].size * partners[iteratePartners].costPerGB; + //appending the minimum usage cost + usageCost = usageCost usageCost ? partners[iteratePartners].partnerId : partnerId; + minimumCost = minimumCost > usageCost ? usageCost : minimumCost; + } + else{ + minimumCost = usageCost; + partnerId = partners[iteratePartners].partnerId; + }} + } + //minusing partner capacity based on the delivery assigned + capacity[partnerId]=capacity[partnerId]-input[iterateInput].size; + console.log("capacityDecreased",capacity); + partnerId = partnerId == undefined ?'""':partnerId; + minimumCost = minimumCost == undefined ?'""':minimumCost; + result = input[iterateInput].id +","+deliverable+","+ partnerId +","+minimumCost; + console.log("result",result); + sortedResult.push(result); + + } + console.log("sortedResult",sortedResult.sort()); + for (let writeFile of sortedResult){ + //appending the result in output2.csv for every iteration + fs.appendFileSync("output2.csv", writeFile + '\n'); + } + + } + +module.exports = { + solution2 +} \ No newline at end of file