From 63f7bc7013f975fb34318c599a25da254fda8ce6 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 24 Mar 2024 11:01:07 -0400 Subject: [PATCH 01/43] init branch From b834b480d5ec1195f8888c33aeb8050702279acf Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 24 Mar 2024 11:27:23 -0400 Subject: [PATCH 02/43] Add README.md --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..a908fdb6 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +Learning JavaScript Data Structures and Algorithms +=================================================== + +Source code of **Learning JavaScript Data Structures and Algorithms** book. + +# Chapters + +## Part 1: Introduction + +* 01: Introducing Data Structures and Algorithms in JavaScript +* 02: Understanding Big O Notation + +## Part 2: Data Structures + +* 03: Arrays +* 04: Stacks +* 05: Queues and Deques +* 06: Linked Lists +* 07: Sets +* 08: Dictionaries and Hashes +* 09: Recursion +* 10: Trees +* 11: Binary Heap and Heap Sort +* 12: Tries +* 13: Graphs + +## Part 3: Algorithms + +* 14: Sorting Algorithms +* 15: Searching and Shuffling Algorithms +* 16: String Algorithms +* 17: Math Algorithms +* 18: Algorithm Designs and Techniques + +# Project Structure + +You can find the source code organized by chapter under the `src` folder. + +*Each file has the `.js` and the `.ts` extension, so you get both the JavaScript and the Typescript versions of the source code.* + +*Each data structure and algorithm from parts 2 and 3 also have a `_test_`* folder where you can find the respective `Jest` test cases. + +## How to use this repository + +**Install all dependencies** + +``` +npm install +``` + +**Run all tests** + +``` +npm test +``` + +**Run a particular example** + +``` +cd src/01-intro +node 01-hello-world.js +``` + +## πŸ’» Tecnologies + +* JavaScript +* TypeScript +* Jest (tests) + +Happy Coding! \ No newline at end of file From 61955b4be40896baed6596229b9551e004f6cc1c Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 10 Apr 2024 16:21:34 -0400 Subject: [PATCH 03/43] Add .editorconfig file with code formatting settings --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8b1709b6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = LF +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file From f3a91e62bf4aaf8aef198c2e03ea23ec6460e99b Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 10 Apr 2024 18:44:29 -0400 Subject: [PATCH 04/43] Add hello variables example in src/01-intro/01-hello-variables.js --- src/01-intro/01-hello-variables.js | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/01-intro/01-hello-variables.js diff --git a/src/01-intro/01-hello-variables.js b/src/01-intro/01-hello-variables.js new file mode 100644 index 00000000..59b5f3b0 --- /dev/null +++ b/src/01-intro/01-hello-variables.js @@ -0,0 +1,33 @@ +// Path: src/01-intro/01-hello-variables.js + +// hello world +console.log('Hello, World!'); + +// variables +var num = 1; // {1} +num = 3; // {2} + +let myVar = 2; // {3} +myVar = 4; // {4} + +const price = 1.5; // {5} number +const publisher = 'Packt'; // {6} string +const javaScriptBook = true; // {7} boolean +const nullVar = null; // {8} null +let und; // {9} undefined + +console.log('num: ' + num); +console.log('myVar: ' + myVar); +console.log('publisher: ' + publisher); +console.log('javaScriptBook: ' + javaScriptBook); +console.log('price: ' + price); +console.log('nullVar: ' + nullVar); +console.log('und: ' + und); + +const book = { + title: 'Data Structures and Algorithms', // {10} +} +book.title = 'Data Structures and Algorithms in JavaScript'; // {11} +// book = { anotherTitle: 'Data Structures’ } // this will not work {12} + +// to see the output of this file use the command: node src/01-intro/01-hello-variables.js \ No newline at end of file From 1cd518acb7844fc6e7dfcb31e5b20e5c57ac37a0 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 16:45:33 -0400 Subject: [PATCH 05/43] Add conditionals examples in src/01-intro/02-conditionals.js --- src/01-intro/02-conditionals.js | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/01-intro/02-conditionals.js diff --git a/src/01-intro/02-conditionals.js b/src/01-intro/02-conditionals.js new file mode 100644 index 00000000..18399a2a --- /dev/null +++ b/src/01-intro/02-conditionals.js @@ -0,0 +1,48 @@ +// Path: src/01-intro/03-conditionals.js + +/* Example 01 - if-else */ +let number = 0; +if (number === 1) { + console.log('number is equal to 1'); +} else { + console.log('number is not equal to 1, the value of number is ' + number); +} + +/* Example 02 - ternary operator - if..else */ +if (number === 1) { + number--; +} else { + number++; +} + +// is the same as +number === 1 ? number-- : number++; + +/* Example 03 - if-else-if-else... */ +let month = 5; +if (month === 1) { + console.log('January'); +} else if (month === 2) { + console.log('February'); +} else if (month === 3) { + console.log('March'); +} else { + console.log('Month is not January, February or March'); +} + +/* Example 05 - switch */ +switch (month) { + case 1: + console.log('January'); + break; + case 2: + console.log('February'); + break; + case 3: + console.log('March'); + break; + default: + console.log('Month is not January, February or March'); +} + +// to see the output of this file use the command: node src/01-intro/03-conditionals.js \ No newline at end of file From d2548701fe621d1fac6c1ab0ab78f4af787429f4 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 16:46:53 -0400 Subject: [PATCH 06/43] Add loops examples in src/01-intro/03-loops.js --- src/01-intro/03-loops.js | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/01-intro/03-loops.js diff --git a/src/01-intro/03-loops.js b/src/01-intro/03-loops.js new file mode 100644 index 00000000..7aeb3599 --- /dev/null +++ b/src/01-intro/03-loops.js @@ -0,0 +1,41 @@ +// Path: src/01-intro/04-loops.js + +console.log('**** for example ****'); +/* for - example */ +for (let i = 0; i < 10; i++) { + console.log(i); +} + +console.log('**** while example ****'); +/* while - example */ +let i = 0; +while (i < 10) { + console.log(i); + i++; +} + +console.log('**** do-while example ****'); +/* do-while - example */ +i = 0; +do { + console.log(i); + i++; +} while (i < 10); + +console.log('**** for-in example ****'); +/* for-in - example */ +const obj = { a: 1, b: 2, c: 3 }; +for (const key in obj) { + console.log(key, obj[key]); +} +// output: a 1 b 2 c 3 + +console.log('**** for-of example ****'); +/* for-of - example */ +const arr = [1, 2, 3]; +for (const value of arr) { + console.log(value); +} +// output: 1 2 3 + +// to see the output of this file use the command: node src/01-intro/04-loops.js \ No newline at end of file From 3fcf815925fa09fbfac4ee5b3db64450f141b573 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 16:51:38 -0400 Subject: [PATCH 07/43] Update file paths in src/01-intro/02-conditionals.js and src/01-intro/03-loops.js --- src/01-intro/02-conditionals.js | 4 ++-- src/01-intro/03-loops.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/01-intro/02-conditionals.js b/src/01-intro/02-conditionals.js index 18399a2a..edc9b7bb 100644 --- a/src/01-intro/02-conditionals.js +++ b/src/01-intro/02-conditionals.js @@ -1,4 +1,4 @@ -// Path: src/01-intro/03-conditionals.js +// Path: src/01-intro/02-conditionals.js /* Example 01 - if-else */ let number = 0; @@ -45,4 +45,4 @@ switch (month) { console.log('Month is not January, February or March'); } -// to see the output of this file use the command: node src/01-intro/03-conditionals.js \ No newline at end of file +// to see the output of this file use the command: node src/01-intro/02-conditionals.js \ No newline at end of file diff --git a/src/01-intro/03-loops.js b/src/01-intro/03-loops.js index 7aeb3599..5ba8ff9e 100644 --- a/src/01-intro/03-loops.js +++ b/src/01-intro/03-loops.js @@ -1,4 +1,4 @@ -// Path: src/01-intro/04-loops.js +// Path: src/01-intro/03-loops.js console.log('**** for example ****'); /* for - example */ @@ -38,4 +38,4 @@ for (const value of arr) { } // output: 1 2 3 -// to see the output of this file use the command: node src/01-intro/04-loops.js \ No newline at end of file +// to see the output of this file use the command: node src/01-intro/03-loops.js \ No newline at end of file From a36d41348e78562b8c2164b94987e5215e865a19 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 16:55:12 -0400 Subject: [PATCH 08/43] Add functions examples in src/01-intro/04-functions.js --- src/01-intro/04-functions.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/01-intro/04-functions.js diff --git a/src/01-intro/04-functions.js b/src/01-intro/04-functions.js new file mode 100644 index 00000000..c49b34a4 --- /dev/null +++ b/src/01-intro/04-functions.js @@ -0,0 +1,24 @@ +// Path: src/01-intro/04-functions.js + +function sayHello(name) { + console.log('Hello! ', name); +} + +sayHello('Packt'); // Hello! Packt + +/* function using the return statement */ +function sum(num1, num2) { + return num1 + num2; +} + +var result = sum(1, 2); +console.log(result); // outputs 3 + +/* function with default parameter */ +function sumDefault(num1, num2 = 2) { // num2 has a default value + return num1 + num2; +} +console.log(sumDefault(1)); // outputs 3 +console.log(sumDefault(1, 3)); // outputs 4 + +// to see the output of this file use the command: node src/01-intro/04-functions.js \ No newline at end of file From f1c12cb3a96af07e4a797e3877e1bccff090faff Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 17:28:20 -0400 Subject: [PATCH 09/43] Add scope examples in src/01-intro/05-scope.js --- src/01-intro/05-scope.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/01-intro/05-scope.js diff --git a/src/01-intro/05-scope.js b/src/01-intro/05-scope.js new file mode 100644 index 00000000..038282c1 --- /dev/null +++ b/src/01-intro/05-scope.js @@ -0,0 +1,34 @@ +// Path: src/01-intro/05-scope.js + +let movie = 'Lord of the Rings'; // {1} + +function starWarsFan() { + const movie = 'Star Wars'; // {2} + return movie; +} + +function marvelFan() { + movie = 'The Avengers'; // {3} + return movie; +} + +function blizzardFan() { + const isFan = true; + let phrase = 'Warcraft'; // {4} + console.log('Before if: ' + phrase); + if (isFan) { + let phrase = 'initial text'; // {5} + phrase = 'For the Horde!'; // {6} + console.log('Inside if: ' + phrase); + } + phrase = 'For the Alliance!'; // {7} + console.log('After if: ' + phrase); +} + +console.log(movie); // Lord of the Rings +console.log(starWarsFan()); // Star Wars +console.log(marvelFan()); // The Avengers +console.log(movie); // The Avengers +blizzardFan(); // Before if: Warcraft, Inside if: For the Horde!, After if: For the Alliance! + +// to see the output of this file use the command: node src/01-intro/05-scope.js From 6c2feac26dc0d54cee6eab08b9b755510747d49a Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 18:05:46 -0400 Subject: [PATCH 10/43] Add objects examples in src/01-intro/06-objects.js --- src/01-intro/06-objects.js | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/01-intro/06-objects.js diff --git a/src/01-intro/06-objects.js b/src/01-intro/06-objects.js new file mode 100644 index 00000000..17c8d6d3 --- /dev/null +++ b/src/01-intro/06-objects.js @@ -0,0 +1,64 @@ +// Path: src/01-intro/06-objects.js + +/* Object example */ +let obj = new Object(); +obj = {}; +obj = { + name: { + first: 'Gandalf', + last: 'the Grey' + }, + address: 'Middle Earth' +}; + + +/* Class example */ +class Book { + + #percentagePerSale = 0.12; // {1} + + constructor(title, pages, isbn) { + this.title = title; // {2} + this.pages = pages; + this.isbn = isbn; + } + + get price() { // {3} + return this.pages * this.#percentagePerSale; + } + + static copiesSold = 0; // {4} + static sellCopy() { // {5} + this.copiesSold++; + } + + printIsbn() { // {6} + console.log(this.isbn); + } +} + +let myBook = new Book('title', 400, 'isbn'); + +console.log(myBook.title); // outputs the book title +myBook.title = 'new title'; // update the value of the book title +console.log(myBook.title); // outputs the updated value: new title + +console.log(myBook.price); // 48 + +console.log(Book.copiesSold); // 0 +Book.sellCopy(); +console.log(Book.copiesSold); // 1 + +Book.sellCopy(); +console.log(Book.copiesSold); // 2 + +class Ebook extends Book { // {7} + constructor(title, pages, isbn, format) { + super(title, pages, isbn); // {8} + this.format = format; // {9} + } +} +Ebook.sellCopy(); // {10} +console.log(Ebook.copiesSold); // {11} 3 + +// to see the output of this file use the command: node src/01-intro/06-objects.js From 22d86dc130efc9fffae829dba1ef976869c14b1d Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 18:33:08 -0400 Subject: [PATCH 11/43] Refactor code in src/01-intro/07-modern.js to use arrow functions, spread operator, rest operator, and exponentiation operator --- src/01-intro/07-modern.js | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/01-intro/07-modern.js diff --git a/src/01-intro/07-modern.js b/src/01-intro/07-modern.js new file mode 100644 index 00000000..a9df190a --- /dev/null +++ b/src/01-intro/07-modern.js @@ -0,0 +1,41 @@ +// Path: src/01-intro/07-modern.js + +/* Arrow function example */ +const circleAreaFn = function circleArea(radius) { + const PI = 3.14; + const area = PI * radius * radius; + return area; +}; +console.log(circleAreaFn(2)); // 12.56 + +// refactoring to use arrow function +const circleArea = (radius) => { // {1} + const PI = 3.14; + return PI * radius * radius; +}; + +// simplified version +const circleAreaSimp = radius => 3.14 * radius * radius; +console.log(circleAreaSimp(2)); // 12.56 + +// no parameters +const hello = () => console.log('hello!'); +hello(); // hello! + +/* Spread operator example */ +const sum = (x, y, z) => x + y + z; + +const numbers = [1, 2, 3]; +console.log(sum(...numbers)); // 6 + +/* Rest operator example */ +const restParamaterFunction = (x, y, ...a) => (x + y) * a.length; +console.log(restParamaterFunction(1, 2, 'hello', true, 7)); // 9 + +/* Exponentiation operator example */ +const r = 2; +let area = 3.14 * r * r; +area = 3.14 * Math.pow(r, 2); +area = 3.14 * r ** 2; + +// to see the output of this file use the command: node src/01-intro/07-modern.js \ No newline at end of file From 1ba0e185c2c26019d84dd5bd989c109ab5f8cb9e Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Apr 2024 18:46:06 -0400 Subject: [PATCH 12/43] Add .gitignore, package-lock.json, and package.json files --- .gitignore | 1 + package-lock.json | 28 ++++++++++++++++++++++++++++ package.json | 12 ++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..40b878db --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..0d4bee8b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "javascript-datastructures-algorithms", + "version": "4.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "javascript-datastructures-algorithms", + "version": "4.0.0", + "devDependencies": { + "typescript": "^5.4.5" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..6f7f9f91 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "javascript-datastructures-algorithms", + "version": "4.0.0", + "description": "Learning JavaScript Data Structures and Algorithms", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Loiane Groner", + "devDependencies": { + "typescript": "^5.4.5" + } +} From 3c129f192a46bca299416cf30b73d7930c7ce546 Mon Sep 17 00:00:00 2001 From: Loiane Date: Tue, 16 Apr 2024 08:23:06 -0400 Subject: [PATCH 13/43] Refactor code in src/01-intro/07-modern.js to use arrow function and add JSDoc comments --- src/01-intro/07-modern.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/01-intro/07-modern.js b/src/01-intro/07-modern.js index a9df190a..8f6d7165 100644 --- a/src/01-intro/07-modern.js +++ b/src/01-intro/07-modern.js @@ -1,11 +1,17 @@ +// @ts-check // Path: src/01-intro/07-modern.js -/* Arrow function example */ +/** + * Arrow function example, calculates the area of a circle + * @param {number} radius + * @returns + */ const circleAreaFn = function circleArea(radius) { const PI = 3.14; const area = PI * radius * radius; return area; }; +// circleAreaFn('book'); console.log(circleAreaFn(2)); // 12.56 // refactoring to use arrow function From 51c93b420e46e6a603c432323c91f188c76fd4cc Mon Sep 17 00:00:00 2001 From: Loiane Date: Tue, 16 Apr 2024 08:23:18 -0400 Subject: [PATCH 14/43] Add TypeScript code examples in src/01-intro/08-typescript.js and src/01-intro/08-typescript.ts --- src/01-intro/08-typescript.js | 17 +++++++++ src/01-intro/08-typescript.ts | 69 +++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/01-intro/08-typescript.js create mode 100644 src/01-intro/08-typescript.ts diff --git a/src/01-intro/08-typescript.js b/src/01-intro/08-typescript.js new file mode 100644 index 00000000..d10774aa --- /dev/null +++ b/src/01-intro/08-typescript.js @@ -0,0 +1,17 @@ +// Path: src/01-intro/08-typescript.ts +var myName = 'Packt'; +// myName = 10; // commented to avoid error +/* Type inference */ +var age = 20; // number +var existsFlag = true; // boolean +var language = 'JavaScript'; // string +var favoriteLanguage; +var langs = ['JavaScript', 'Ruby', 'Python']; +favoriteLanguage = langs[0]; +function printName(person) { + console.log(person.name); +} +var john = { name: 'John', age: 21 }; +var mary = { name: 'Mary', age: 21, phone: '123-45678' }; +printName(john); +printName(mary); diff --git a/src/01-intro/08-typescript.ts b/src/01-intro/08-typescript.ts new file mode 100644 index 00000000..96497edc --- /dev/null +++ b/src/01-intro/08-typescript.ts @@ -0,0 +1,69 @@ +// Path: src/01-intro/08-typescript.ts + +let myName = 'Packt'; +// myName = 10; // commented to avoid error + +/* Type inference */ +let age = 20; // number +let existsFlag = true; // boolean +let language = 'JavaScript'; // string + +let favoriteLanguage: string; +let langs = ['JavaScript', 'Ruby', 'Python']; +favoriteLanguage = langs[0]; + +/* Interfaces as type */ +interface Person { + name: string; + age: number; +} + +function printName(person: Person) { + console.log(person.name); +} + +const john = { name: 'John', age: 21 }; +const mary = { name: 'Mary', age: 21, phone: '123-45678' }; +printName(john); +printName(mary); + +/* Interfaces as OO */ +interface Comparable { + compareTo(b: T): number; +} + +class MyObject implements Comparable { + age: number; + + constructor(age: number) { + this.age = age; + } + + compareTo(b: MyObject): number { + if (this.age === b.age) { + return 0; + } + return this.age > b.age ? 1 : -1; + } +} + +/* Enums */ +enum Compare { + LESS_THAN = -1, + BIGGER_THAN = 1, + EQUALS = 0 +} + +function compareTo(a: MyObject, b: MyObject): number { + if (a.age === b.age) { + return Compare.EQUALS; + } + return a.age > b.age ? Compare.BIGGER_THAN : Compare.LESS_THAN; +} + +/* Type aliases */ +type UserID = string; +type User = { + id: UserID; + name: string; +} \ No newline at end of file From 8652345e580b2f3c09398f00e5fbedfb7cd1ce58 Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 26 Apr 2024 18:38:16 -0400 Subject: [PATCH 15/43] Add Big O notation examples in src/02-bigOnotation/01-big-o-intro.js, 02-bigOChart.html, and 02-bigOChart.js --- src/02-bigOnotation/01-big-o-intro.js | 76 +++++++++++++++++++++++++++ src/02-bigOnotation/02-bigOChart.html | 14 +++++ src/02-bigOnotation/02-bigOChart.js | 39 ++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/02-bigOnotation/01-big-o-intro.js create mode 100644 src/02-bigOnotation/02-bigOChart.html create mode 100644 src/02-bigOnotation/02-bigOChart.js diff --git a/src/02-bigOnotation/01-big-o-intro.js b/src/02-bigOnotation/01-big-o-intro.js new file mode 100644 index 00000000..a1b821f2 --- /dev/null +++ b/src/02-bigOnotation/01-big-o-intro.js @@ -0,0 +1,76 @@ +// Path: src/02-bigOnotation/01-big-o-intro.js + +// O(1) - Constant Time +function multiplyBy3(num) { + console.log(`${num} * 3 = `, num * 3); +} + +console.log('O(1) - Constant Time'); +multiplyBy3(5); +multiplyBy3(50); + +// O(n) - Linear Time +function multiplicationTable(num, x) { + for (let i = 1; i <= x; i++) { + console.log(`${num} * ${i} = `, num * i); + } +} + +console.log('*******************'); +console.log('O(n) - Linear Time'); +console.log('Multiplication table for 5 with x = 3'); +multiplicationTable(5, 3); + +console.log('Multiplication table for 5 with x = 10'); +multiplicationTable(5, 10); + +// O(n^2) - Quadratic Time +function multiplicationTable2(num, x) { + for (let i = 1; i <= num; i++) { + console.log(`Multiplication table for ${i} with x = ${x}`); + for (let j = 1; j <= x; j++) { + console.log(`${i} * ${j} = `, i * j); + } + } +} + +console.log('************************'); +console.log('O(n^2) - Quadratic Time'); +multiplicationTable2(3, 2); + +// O(nΛ†3) - Cubic Time +function multiplicationTable3(num, x) { + for (let i = 1; i <= num; i++) { + for (let j = 1; j <= x; j++) { + for (let k = 1; k <= x; k++) { + console.log(`${i} * ${j} * ${k} = `, i * j * k); + } + } + } +} + +console.log('************************'); +console.log('O(n^3) - Cubic Time'); +multiplicationTable3(2, 3); + +// calculating the time complexity of the function +function multiplicationTableRefactored(num, x) { + + let s = ''; // {1} + let numberOfAsterisks = num * x; // {2} + for (let i = 1; i <= numberOfAsterisks; i++) { // {3} + s += '*'; + } + console.log(s); // {4} + console.log('Calculating the time complexity of a function'); // {5} + + for (let i = 1; i <= num; i++) { // {6} + console.log(`Multiplication table for ${i} with x = ${x}`); // {7} + for (let j = 1; j <= x; j++) { // {8} + console.log(`${i} * ${j} = `, i * j); // {9} + } + } +} + +multiplicationTableRefactored(3, 2); +// to see the output of this file use the command: node src/02-bigOnotation/01-big-o-intro.js \ No newline at end of file diff --git a/src/02-bigOnotation/02-bigOChart.html b/src/02-bigOnotation/02-bigOChart.html new file mode 100644 index 00000000..081815fd --- /dev/null +++ b/src/02-bigOnotation/02-bigOChart.html @@ -0,0 +1,14 @@ + + + + + + + + + +
+ + \ No newline at end of file diff --git a/src/02-bigOnotation/02-bigOChart.js b/src/02-bigOnotation/02-bigOChart.js new file mode 100644 index 00000000..f4aa9492 --- /dev/null +++ b/src/02-bigOnotation/02-bigOChart.js @@ -0,0 +1,39 @@ +google.load('visualization', '1.0', {'packages':['corechart']}); +google.setOnLoadCallback(drawChart); + +function drawChart() { + + const data = new google.visualization.DataTable(); + data.addColumn('string', 'n'); + data.addColumn('number', 'O(1)'); + data.addColumn('number', 'O(log n)'); + data.addColumn('number', 'O(n)'); + data.addColumn('number', 'O(n log n)'); + data.addColumn('number', 'O(n^2)'); + data.addColumn('number', 'O(2^n)'); + //data.addColumn('number', 'O(n!)'); + + for (let i = 0; i <= 30; i++) { + data.addRow([i+'', 1, Math.log(i), i, Math.log(i)*i, Math.pow(i,2), Math.pow(2,i)]); + } + + const options = { + 'width':700, + 'height':600, + 'backgroundColor':{stroke:'#CCC',strokeWidth:1}, + 'curveType':'function', + 'legend': { position: 'right'}, + 'hAxis':{ + title:'Elements (n)', + showTextEvery:5 + }, + 'vAxis':{ + title:'Operations', + viewWindowMode:'explicit', + viewWindow:{min:0,max:300} + } + }; + + const chart = new google.visualization.LineChart(document.getElementById('chart_div')); + chart.draw(data, options); +} \ No newline at end of file From 6e1f45ed78f38769b158345ec5dcdcb81240d164 Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 17 May 2024 16:01:49 -0400 Subject: [PATCH 16/43] chapter 3: arrays --- package.json | 1 + src/01-intro/08-typescript.js | 41 +++++-- src/01-intro/08-typescript.ts | 4 +- src/03-array/01-arrays.js | 74 ++++++++++++ src/03-array/01-arrays.ts | 74 ++++++++++++ src/03-array/02-adding-removing-elements.js | 73 ++++++++++++ src/03-array/02-adding-removing-elements.ts | 73 ++++++++++++ src/03-array/03-iterator-functions.js | 45 ++++++++ src/03-array/03-iterator-functions.ts | 45 ++++++++ src/03-array/04-searching-sorting.js | 105 ++++++++++++++++++ src/03-array/04-searching-sorting.ts | 105 ++++++++++++++++++ src/03-array/05-transforming-array.js | 47 ++++++++ src/03-array/05-transforming-array.ts | 47 ++++++++ src/03-array/06-other-methods.js | 74 ++++++++++++ src/03-array/06-other-methods.ts | 74 ++++++++++++ src/03-array/07-multidimensional-arrays.js | 87 +++++++++++++++ src/03-array/07-multidimensional-arrays.ts | 87 +++++++++++++++ src/03-array/08-TypedArrays.js | 15 +++ src/03-array/08-TypedArrays.ts | 15 +++ src/03-array/09-arrays-typescript.js | 14 +++ src/03-array/09-arrays-typescript.ts | 23 ++++ src/03-array/10-todo-list-example.html | 54 +++++++++ src/03-array/hackerrank/01-arrays-ds.js | 31 ++++++ src/03-array/hackerrank/01-arrays-ds.ts | 47 ++++++++ .../hackerrank/02-array-left-rotation.js | 13 +++ .../hackerrank/02-array-left-rotation.ts | 29 +++++ src/04-stack/leetcode/min-stack.ts | 49 ++++++++ src/04-stack/leetcode/simplify-path.ts | 43 +++++++ src/04-stack/leetcode/valid-parentheses.ts | 64 +++++++++++ tsconfig.json | 16 +++ 30 files changed, 1459 insertions(+), 10 deletions(-) create mode 100644 src/03-array/01-arrays.js create mode 100644 src/03-array/01-arrays.ts create mode 100644 src/03-array/02-adding-removing-elements.js create mode 100644 src/03-array/02-adding-removing-elements.ts create mode 100644 src/03-array/03-iterator-functions.js create mode 100644 src/03-array/03-iterator-functions.ts create mode 100644 src/03-array/04-searching-sorting.js create mode 100644 src/03-array/04-searching-sorting.ts create mode 100644 src/03-array/05-transforming-array.js create mode 100644 src/03-array/05-transforming-array.ts create mode 100644 src/03-array/06-other-methods.js create mode 100644 src/03-array/06-other-methods.ts create mode 100644 src/03-array/07-multidimensional-arrays.js create mode 100644 src/03-array/07-multidimensional-arrays.ts create mode 100644 src/03-array/08-TypedArrays.js create mode 100644 src/03-array/08-TypedArrays.ts create mode 100644 src/03-array/09-arrays-typescript.js create mode 100644 src/03-array/09-arrays-typescript.ts create mode 100644 src/03-array/10-todo-list-example.html create mode 100644 src/03-array/hackerrank/01-arrays-ds.js create mode 100644 src/03-array/hackerrank/01-arrays-ds.ts create mode 100644 src/03-array/hackerrank/02-array-left-rotation.js create mode 100644 src/03-array/hackerrank/02-array-left-rotation.ts create mode 100644 src/04-stack/leetcode/min-stack.ts create mode 100644 src/04-stack/leetcode/simplify-path.ts create mode 100644 src/04-stack/leetcode/valid-parentheses.ts create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 6f7f9f91..1f51d4fd 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "4.0.0", "description": "Learning JavaScript Data Structures and Algorithms", "scripts": { + "build:ts": "tsc -p tsconfig.json", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Loiane Groner", diff --git a/src/01-intro/08-typescript.js b/src/01-intro/08-typescript.js index d10774aa..284054fd 100644 --- a/src/01-intro/08-typescript.js +++ b/src/01-intro/08-typescript.js @@ -1,17 +1,42 @@ // Path: src/01-intro/08-typescript.ts -var myName = 'Packt'; +let myName = 'Packt'; // myName = 10; // commented to avoid error /* Type inference */ -var age = 20; // number -var existsFlag = true; // boolean -var language = 'JavaScript'; // string -var favoriteLanguage; -var langs = ['JavaScript', 'Ruby', 'Python']; +let age = 20; // number +let existsFlag = true; // boolean +let language = 'JavaScript'; // string +let favoriteLanguage; +let langs = ['JavaScript', 'Ruby', 'Python']; favoriteLanguage = langs[0]; function printName(person) { console.log(person.name); } -var john = { name: 'John', age: 21 }; -var mary = { name: 'Mary', age: 21, phone: '123-45678' }; +const john = { name: 'John', age: 21 }; +const mary = { name: 'Mary', age: 21, phone: '123-45678' }; printName(john); printName(mary); +class MyObject { + age; + constructor(age) { + this.age = age; + } + compareTo(b) { + if (this.age === b.age) { + return 0; + } + return this.age > b.age ? 1 : -1; + } +} +/* Enums */ +var Compare; +(function (Compare) { + Compare[Compare["LESS_THAN"] = -1] = "LESS_THAN"; + Compare[Compare["BIGGER_THAN"] = 1] = "BIGGER_THAN"; + Compare[Compare["EQUALS"] = 0] = "EQUALS"; +})(Compare || (Compare = {})); +function compareTo(a, b) { + if (a.age === b.age) { + return Compare.EQUALS; + } + return a.age > b.age ? Compare.BIGGER_THAN : Compare.LESS_THAN; +} diff --git a/src/01-intro/08-typescript.ts b/src/01-intro/08-typescript.ts index 96497edc..3f4cdee5 100644 --- a/src/01-intro/08-typescript.ts +++ b/src/01-intro/08-typescript.ts @@ -13,12 +13,12 @@ let langs = ['JavaScript', 'Ruby', 'Python']; favoriteLanguage = langs[0]; /* Interfaces as type */ -interface Person { +interface Friend { name: string; age: number; } -function printName(person: Person) { +function printName(person: Friend) { console.log(person.name); } diff --git a/src/03-array/01-arrays.js b/src/03-array/01-arrays.js new file mode 100644 index 00000000..0458a07a --- /dev/null +++ b/src/03-array/01-arrays.js @@ -0,0 +1,74 @@ +// Path: src/03-array/01-arrays.js + +/* Arrays Intro */ +const averageTempJan = 12; +const averageTempFeb = 15; +const averageTempMar = 18; +const averageTempApr = 20; +const averageTempMay = 25; + +const averageTemp = [12, 15, 18, 20, 25]; +// or +averageTemp[0] = 12; +averageTemp[1] = 15; +averageTemp[2] = 18; +averageTemp[3] = 20; +averageTemp[4] = 25; + +console.log('averageTempJan', averageTempJan); +console.log('averageTempFeb', averageTempFeb); +console.log('averageTempMar', averageTempMar); +console.log('averageTempApr', averageTempApr); +console.log('averageTempMay', averageTempMay); + +console.log('averageTemp[0]', averageTemp[0]); +console.log('averageTemp[1]', averageTemp[1]); +console.log('averageTemp[2]', averageTemp[2]); +console.log('averageTemp[3]', averageTemp[3]); +console.log('averageTemp[4]', averageTemp[4]); + +/* Creating and initializing arrays */ +let daysOfWeek = new Array(); // {1} +daysOfWeek = new Array(7); // {2} +daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3} + +// preferred +daysOfWeek = []; // {4} +daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // {5} + +console.log('daysOfWeek.length', daysOfWeek.length); // output: 7 + +for (let i = 0; i < daysOfWeek.length; i++) { + console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); +} + +/* fibonacci numbers */ +// Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... +const fibonacci = []; // {1} +fibonacci[1] = 1; // {2} +fibonacci[2] = 1; // {3} + +// create the fibonacci sequence starting from the 3rd element +for (let i = 3; i < 20; i++) { + fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; // //{4} +} + +// display the fibonacci sequence +for (let i = 1; i < fibonacci.length; i++) { // {5} + console.log(`fibonacci[${i}]`, fibonacci[i]); // {6} +} + +// instead of {5} and {6} we can use +console.log('fibonacci', fibonacci); + +// Using the for..in loop +for (const i in fibonacci) { + console.log(`fibonacci[${i}]`, fibonacci[i]); +} + +// Using the for..of loop +for (const value of fibonacci) { + console.log('value', value); +} + +// to see the output of this file use the command: node src/03-array/01-arrays.js \ No newline at end of file diff --git a/src/03-array/01-arrays.ts b/src/03-array/01-arrays.ts new file mode 100644 index 00000000..f33e8c5f --- /dev/null +++ b/src/03-array/01-arrays.ts @@ -0,0 +1,74 @@ +// Path: src/03-array/01-arrays.ts + +/* Arrays Intro */ +const averageTempJan = 12; +const averageTempFeb = 15; +const averageTempMar = 18; +const averageTempApr = 20; +const averageTempMay = 25; + +const averageTemp = [12, 15, 18, 20, 25]; +// or +averageTemp[0] = 12; +averageTemp[1] = 15; +averageTemp[2] = 18; +averageTemp[3] = 20; +averageTemp[4] = 25; + +console.log('averageTempJan', averageTempJan); +console.log('averageTempFeb', averageTempFeb); +console.log('averageTempMar', averageTempMar); +console.log('averageTempApr', averageTempApr); +console.log('averageTempMay', averageTempMay); + +console.log('averageTemp[0]', averageTemp[0]); +console.log('averageTemp[1]', averageTemp[1]); +console.log('averageTemp[2]', averageTemp[2]); +console.log('averageTemp[3]', averageTemp[3]); +console.log('averageTemp[4]', averageTemp[4]); + +/* Creating and initializing arrays */ +let daysOfWeek = new Array(); // {1} +daysOfWeek = new Array(7); // {2} +daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3} + +// preferred +daysOfWeek = []; // {4} +daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // {5} + +console.log('daysOfWeek.length', daysOfWeek.length); // output: 7 + +for (let i = 0; i < daysOfWeek.length; i++) { + console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); +} + +/* fibonacci numbers */ +// Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... +const fibonacci = []; // {1} +fibonacci[1] = 1; // {2} +fibonacci[2] = 1; // {3} + +// create the fibonacci sequence starting from the 3rd element +for (let i = 3; i < 20; i++) { + fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; // //{4} +} + +// display the fibonacci sequence +for (let i = 1; i < fibonacci.length; i++) { // {5} + console.log(`fibonacci[${i}]`, fibonacci[i]); // {6} +} + +// instead of {5} and {6} we can use +console.log('fibonacci', fibonacci); + +// Using the for..in loop +for (const i in fibonacci) { + console.log(`fibonacci[${i}]`, fibonacci[i]); +} + +// Using the for..of loop +for (const value of fibonacci) { + console.log('value', value); +} + +// to see the output of this file use the command: node src/03-array/01-arrays.ts \ No newline at end of file diff --git a/src/03-array/02-adding-removing-elements.js b/src/03-array/02-adding-removing-elements.js new file mode 100644 index 00000000..34fdd30a --- /dev/null +++ b/src/03-array/02-adding-removing-elements.js @@ -0,0 +1,73 @@ +// Path: src/03-array/02-adding-removing-elements.js + +// @ts-ignore +let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +numbers[numbers.length] = 10; + +// using push method +numbers.push(11); +numbers.push(12, 13); + +// inserting elements at the beginning +// @ts-ignore +Array.prototype.insertAtBeginning = function (value) { + for (let i = this.length; i >= 0; i--) { + this[i] = this[i - 1]; + } + this[0] = value; +}; +// @ts-ignore +numbers.insertAtBeginning(-1); + +// using unshift method +numbers.unshift(-2); +numbers.unshift(-4, -3); + +// removing elements from the end +numbers.pop(); // number 13 is removed +// console.log('Removed element: ', numbers.pop()); // Removed element: 13 +console.log('array length: ', numbers.length); // array length: 17 + +// removing elements from the beginning +for (let i = 0; i < numbers.length; i++) { + // numbers[i] = numbers[i + 1]; +} + +// removing elements from the beginning - educational purposes only +// @ts-ignore +Array.prototype.reIndex = function (myArray) { + const newArray = []; + for (let i = 0; i < myArray.length; i++) { + if (myArray[i] !== undefined) { + newArray.push(myArray[i]); + } + } + return newArray; +} +// remove first position manually and reIndex +// @ts-ignore +Array.prototype.removeFromBeginning = function () { + for (let i = 0; i < this.length; i++) { + this[i] = this[i + 1]; + } + // @ts-ignore + return this.reIndex(this); +}; +// @ts-ignore +// numbers = numbers.removeFromBeginning(); + +// using shift method +numbers.shift(); +console.log('numbers after shift: ', numbers); +console.log('array length: ', numbers.length); // array length: 16 + +// adding and removing elements from a specific position +// using the splice method +numbers.splice(5, 3); // removes 3 elements starting from the 5th position +console.log('numbers: ', numbers); // numbers: [ -3, -2, -1, 0, 1, 5, 6, 7, 8, 9, 10, 11, 12 ] + +// adding elements, 2, 3 and 4 at the 5th position +numbers.splice(5, 0, 2, 3, 4); + +// to see the output of this file use the command: node src/03-array/02-adding-removing-elements.js diff --git a/src/03-array/02-adding-removing-elements.ts b/src/03-array/02-adding-removing-elements.ts new file mode 100644 index 00000000..bd9ff917 --- /dev/null +++ b/src/03-array/02-adding-removing-elements.ts @@ -0,0 +1,73 @@ +// Path: src/03-array/02-adding-removing-elements.ts + +// @ts-ignore +let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +numbers[numbers.length] = 10; + +// using push method +numbers.push(11); +numbers.push(12, 13); + +// inserting elements at the beginning +// @ts-ignore +Array.prototype.insertAtBeginning = function (value) { + for (let i = this.length; i >= 0; i--) { + this[i] = this[i - 1]; + } + this[0] = value; +}; +// @ts-ignore +numbers.insertAtBeginning(-1); + +// using unshift method +numbers.unshift(-2); +numbers.unshift(-4, -3); + +// removing elements from the end +numbers.pop(); // number 13 is removed +// console.log('Removed element: ', numbers.pop()); // Removed element: 13 +console.log('array length: ', numbers.length); // array length: 17 + +// removing elements from the beginning +for (let i = 0; i < numbers.length; i++) { + // numbers[i] = numbers[i + 1]; +} + +// removing elements from the beginning - educational purposes only +// @ts-ignore +Array.prototype.reIndex = function (myArray) { + const newArray = []; + for (let i = 0; i < myArray.length; i++) { + if (myArray[i] !== undefined) { + newArray.push(myArray[i]); + } + } + return newArray; +} +// remove first position manually and reIndex +// @ts-ignore +Array.prototype.removeFromBeginning = function () { + for (let i = 0; i < this.length; i++) { + this[i] = this[i + 1]; + } + // @ts-ignore + return this.reIndex(this); +}; +// @ts-ignore +// numbers = numbers.removeFromBeginning(); + +// using shift method +numbers.shift(); +console.log('numbers after shift: ', numbers); +console.log('array length: ', numbers.length); // array length: 16 + +// adding and removing elements from a specific position +// using the splice method +numbers.splice(5, 3); // removes 3 elements starting from the 5th position +console.log('numbers: ', numbers); // numbers: [ -3, -2, -1, 0, 1, 5, 6, 7, 8, 9, 10, 11, 12 ] + +// adding elements, 2, 3 and 4 at the 5th position +numbers.splice(5, 0, 2, 3, 4); + +// to see the output of this file use the command: node src/03-array/02-adding-removing-elements.ts diff --git a/src/03-array/03-iterator-functions.js b/src/03-array/03-iterator-functions.js new file mode 100644 index 00000000..2ae003b5 --- /dev/null +++ b/src/03-array/03-iterator-functions.js @@ -0,0 +1,45 @@ +// Path: src/03-array/03-iterator-functions.js + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using forEach method +numbers.forEach((value, index) => { + console.log(`numbers[${index}]`, value); +}); + +numbers.forEach(value => console.log(value)); + +// using every method +const isBelowSeven = numbers.every(value => value < 7); +console.log('All values are below 7?:', isBelowSeven); // false + +// rewriting the preceding code using for loop +let isBelowSevenForLoop = true; +for (let i = 0; i < numbers.length; i++) { + if (numbers[i] >= 7) { + isBelowSevenForLoop = false; + break; + } +} +console.log('All values are below 7?:', isBelowSevenForLoop); // false + +// using some method +const isSomeValueBelowSeven = numbers.some(value => value < 7); +console.log('Is any value below 7?:', isSomeValueBelowSeven); // true + +// rewriting the preceding code using for loop +let isSomeValueBelowSevenForLoop = false; +for (let i = 0; i < numbers.length; i++) { + if (numbers[i] < 7) { + isSomeValueBelowSevenForLoop = true; + break; + } +} + +// using filter method +// @ts-ignore +const valuesBelowSeven = numbers.filter(value => value < 7); +console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] + +// to see the output of this file use the command: node src/03-array/03-iterator-functions.js \ No newline at end of file diff --git a/src/03-array/03-iterator-functions.ts b/src/03-array/03-iterator-functions.ts new file mode 100644 index 00000000..963288de --- /dev/null +++ b/src/03-array/03-iterator-functions.ts @@ -0,0 +1,45 @@ +// Path: src/03-array/03-iterator-functions.ts + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using forEach method +numbers.forEach((value, index) => { + console.log(`numbers[${index}]`, value); +}); + +numbers.forEach(value => console.log(value)); + +// using every method +const isBelowSeven = numbers.every(value => value < 7); +console.log('All values are below 7?:', isBelowSeven); // false + +// rewriting the preceding code using for loop +let isBelowSevenForLoop = true; +for (let i = 0; i < numbers.length; i++) { + if (numbers[i] >= 7) { + isBelowSevenForLoop = false; + break; + } +} +console.log('All values are below 7?:', isBelowSevenForLoop); // false + +// using some method +const isSomeValueBelowSeven = numbers.some(value => value < 7); +console.log('Is any value below 7?:', isSomeValueBelowSeven); // true + +// rewriting the preceding code using for loop +let isSomeValueBelowSevenForLoop = false; +for (let i = 0; i < numbers.length; i++) { + if (numbers[i] < 7) { + isSomeValueBelowSevenForLoop = true; + break; + } +} + +// using filter method +// @ts-ignore +const valuesBelowSeven = numbers.filter(value => value < 7); +console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] + +// to see the output of this file use the command: node src/03-array/03-iterator-functions.ts \ No newline at end of file diff --git a/src/03-array/04-searching-sorting.js b/src/03-array/04-searching-sorting.js new file mode 100644 index 00000000..25fa2f97 --- /dev/null +++ b/src/03-array/04-searching-sorting.js @@ -0,0 +1,105 @@ +// Path: src/03-array/04-searching-sorting.js + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using indexOf method +console.log('Index of 5:', numbers.indexOf(5)); // 4 +console.log('Index of 11:', numbers.indexOf(11)); // -1 + +// using lastIndexOf method +console.log('Last index of 5:', numbers.lastIndexOf(5)); // 4 +console.log('Last index of 11:', numbers.lastIndexOf(11)); // -1 + +// using includes method +console.log('Is 5 included?:', numbers.includes(5)); // true +console.log('Is 11 included?:', numbers.includes(11)); // false + +// using find, findIndex and findLastIndex methods +const firstValueBelowSeven = numbers.find(value => value < 7); +console.log('First value below 7:', firstValueBelowSeven); // 1 +console.log('Index of first value below 7:', numbers.findIndex(value => value < 7)); // 0 +console.log('Index of last value below 7:', numbers.findLastIndex(value => value < 7)); // 5 + +const books = [ + { id: 1, title: 'The Fellowship of the Ring' }, + { id: 2, title: 'Fourth Wing' }, + { id: 3, title: 'A Court of Thorns and Roses' } +]; +console.log('Book with id 2:', books.find(book => book.id === 2)); // { id: 2, title: 'Fourth Wing' } +console.log('The Hobbit:', books.find(book => book.title === 'The Hobbit')); // undefined + +// remove book with id 3 +const bookIndex = books.findIndex(book => book.id === 3); +if (bookIndex !== -1) { + books.splice(bookIndex, 1); +} + +// using the filter method in the numbers array +// @ts-ignore +const valuesBelowSeven = numbers.filter(value => value < 7); +console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] + +// reverse the array +numbers.reverse(); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +console.log('Reversed numbers:', numbers); + +// sort the array +numbers.sort(); // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9] + +numbers.sort((a, b) => a - b); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +// @ts-ignore +function compareNumbers(a, b) { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + // a must be equal to b + return 0; + } + numbers.sort(compareNumbers); + +console.log('Sorted numbers:', numbers); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +// custom sorting +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; +// @ts-ignore +const compareFriends = (friendA, friendB) => friendA.age - friendB.age; +friends.sort(compareFriends); +console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] + +// sorting strings +// @ts-ignore +let names = ['Ana', 'ana', 'john', 'John']; +console.log(names.sort()); // ['Ana', 'John', 'ana', 'john'] + +names = ['Ana', 'ana', 'john', 'John']; // reset the array to its original state +names.sort((a, b) => { + const nameA = a.toLowerCase(); + const nameB = b.toLowerCase(); + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; +}); +console.log(names); // ['Ana', 'ana', 'John', 'john'] + +names.sort((a, b) => a.localeCompare(b)); +console.log(names); // ['ana', 'Ana', 'john', 'John'] + +const names2 = ['Maève', 'Maeve']; +console.log(names2.sort((a, b) => a.localeCompare(b))); // ['Maeve', 'Maève'] + + +// to see the output of this file use the command: node src/03-array/04-searching-sorting.js \ No newline at end of file diff --git a/src/03-array/04-searching-sorting.ts b/src/03-array/04-searching-sorting.ts new file mode 100644 index 00000000..8dd73db7 --- /dev/null +++ b/src/03-array/04-searching-sorting.ts @@ -0,0 +1,105 @@ +// Path: src/03-array/04-searching-sorting.ts + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using indexOf method +console.log('Index of 5:', numbers.indexOf(5)); // 4 +console.log('Index of 11:', numbers.indexOf(11)); // -1 + +// using lastIndexOf method +console.log('Last index of 5:', numbers.lastIndexOf(5)); // 4 +console.log('Last index of 11:', numbers.lastIndexOf(11)); // -1 + +// using includes method +console.log('Is 5 included?:', numbers.includes(5)); // true +console.log('Is 11 included?:', numbers.includes(11)); // false + +// using find, findIndex and findLastIndex methods +const firstValueBelowSeven = numbers.find(value => value < 7); +console.log('First value below 7:', firstValueBelowSeven); // 1 +console.log('Index of first value below 7:', numbers.findIndex(value => value < 7)); // 0 +console.log('Index of last value below 7:', numbers.findLastIndex(value => value < 7)); // 5 + +const books = [ + { id: 1, title: 'The Fellowship of the Ring' }, + { id: 2, title: 'Fourth Wing' }, + { id: 3, title: 'A Court of Thorns and Roses' } +]; +console.log('Book with id 2:', books.find(book => book.id === 2)); // { id: 2, title: 'Fourth Wing' } +console.log('The Hobbit:', books.find(book => book.title === 'The Hobbit')); // undefined + +// remove book with id 3 +const bookIndex = books.findIndex(book => book.id === 3); +if (bookIndex !== -1) { + books.splice(bookIndex, 1); +} + +// using the filter method in the numbers array +// @ts-ignore +const valuesBelowSeven = numbers.filter(value => value < 7); +console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] + +// reverse the array +numbers.reverse(); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +console.log('Reversed numbers:', numbers); + +// sort the array +numbers.sort(); // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9] + +numbers.sort((a, b) => a - b); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +// @ts-ignore +function compareNumbers(a, b) { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + // a must be equal to b + return 0; + } + numbers.sort(compareNumbers); + +console.log('Sorted numbers:', numbers); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +// custom sorting +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; +// @ts-ignore +const compareFriends = (friendA, friendB) => friendA.age - friendB.age; +friends.sort(compareFriends); +console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] + +// sorting strings +// @ts-ignore +let names = ['Ana', 'ana', 'john', 'John']; +console.log(names.sort()); // ['Ana', 'John', 'ana', 'john'] + +names = ['Ana', 'ana', 'john', 'John']; // reset the array to its original state +names.sort((a, b) => { + const nameA = a.toLowerCase(); + const nameB = b.toLowerCase(); + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; +}); +console.log(names); // ['Ana', 'ana', 'John', 'john'] + +names.sort((a, b) => a.localeCompare(b)); +console.log(names); // ['ana', 'Ana', 'john', 'John'] + +const names2 = ['Maève', 'Maeve']; +console.log(names2.sort((a, b) => a.localeCompare(b))); // ['Maeve', 'Maève'] + + +// to see the output of this file use the command: node src/03-array/04-searching-sorting.ts \ No newline at end of file diff --git a/src/03-array/05-transforming-array.js b/src/03-array/05-transforming-array.js new file mode 100644 index 00000000..e08178a5 --- /dev/null +++ b/src/03-array/05-transforming-array.js @@ -0,0 +1,47 @@ +// Path: src/03-array/05-transforming-array.js + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using the map method +const squaredNumbers = numbers.map(value => value * value); +console.log('Squared numbers:', squaredNumbers); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + +// rewriting the above code using a loop +const squaredNumbersLoop = []; +for (let i = 0; i < numbers.length; i++) { + squaredNumbersLoop.push(numbers[i] * numbers[i]); +} + +// using the split method +const namesFromCSV = 'Aelin,Gandalf,Violet,Poppy'; +// @ts-ignore +const names = namesFromCSV.split(','); +console.log('Names:', names); // ['Aelin', 'Gandalf', 'Violet', 'Poppy'] + +// using the join method +const namesCSV = names.join(';'); +console.log('Names CSV:', namesCSV); // 'Aelin;Gandalf;Violet;Poppy' + +// using the reduce method +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const sum = numbers.reduce((acc, value) => acc + value, 0); // 55 + +// rewriting the above code using a loop +let sumLoop = 0; +for (let i = 0; i < numbers.length; i++) { + sumLoop += numbers[i]; +} + +// using the reduce method to find the maximum value +const scores = [30, 70, 85, 90, 100]; +const highestScore = scores.reduce((max, score) => score > max ? score : max, scores[0]); // 100 + +// using reduceRight method +const reversedNumbers = numbers.reduceRight((acc, value) => { + acc.push(value); + return acc; +}, []); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] + +// to see the output of this file use the command: node src/03-array/05-transforming-array.js \ No newline at end of file diff --git a/src/03-array/05-transforming-array.ts b/src/03-array/05-transforming-array.ts new file mode 100644 index 00000000..43f6e528 --- /dev/null +++ b/src/03-array/05-transforming-array.ts @@ -0,0 +1,47 @@ +// Path: src/03-array/05-transforming-array.ts + +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// using the map method +const squaredNumbers = numbers.map(value => value * value); +console.log('Squared numbers:', squaredNumbers); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + +// rewriting the above code using a loop +const squaredNumbersLoop = []; +for (let i = 0; i < numbers.length; i++) { + squaredNumbersLoop.push(numbers[i] * numbers[i]); +} + +// using the split method +const namesFromCSV = 'Aelin,Gandalf,Violet,Poppy'; +// @ts-ignore +const names = namesFromCSV.split(','); +console.log('Names:', names); // ['Aelin', 'Gandalf', 'Violet', 'Poppy'] + +// using the join method +const namesCSV = names.join(';'); +console.log('Names CSV:', namesCSV); // 'Aelin;Gandalf;Violet;Poppy' + +// using the reduce method +// @ts-ignore +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const sum = numbers.reduce((acc, value) => acc + value, 0); // 55 + +// rewriting the above code using a loop +let sumLoop = 0; +for (let i = 0; i < numbers.length; i++) { + sumLoop += numbers[i]; +} + +// using the reduce method to find the maximum value +const scores = [30, 70, 85, 90, 100]; +const highestScore = scores.reduce((max, score) => score > max ? score : max, scores[0]); // 100 + +// using reduceRight method +const reversedNumbers = numbers.reduceRight((acc, value) => { + acc.push(value); + return acc; +}, []); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] + +// to see the output of this file use the command: node src/03-array/05-transforming-array.ts \ No newline at end of file diff --git a/src/03-array/06-other-methods.js b/src/03-array/06-other-methods.js new file mode 100644 index 00000000..2334d50a --- /dev/null +++ b/src/03-array/06-other-methods.js @@ -0,0 +1,74 @@ +// Path: src/03-array/06-other-methods.js + +// using Array.isArray() method +console.log(typeof 'Learning Data Structures'); // string +console.log(typeof 123); // number +console.log(typeof { id: 1 }); // object +console.log(typeof [1, 2, 3]); // object + +console.log(Array.isArray([1, 2, 3])); // true + +// real world example +const jsonString = JSON.stringify('[{"id":1,"title":"The Fellowship of the Ring"},{"id":2,"title":"Fourth Wing"}]'); +const dataReceived = JSON.parse(jsonString); + +if (Array.isArray(dataReceived)) { + console.log('It is an array'); + // check if The Fellowship of the Ring is in the array + const fellowship = dataReceived.find((item) => { + return item.title === 'The Fellowship of the Ring'; + }); + if (fellowship) { + console.log('We received the book we were looking for!'); + } else { + console.log('We did not receive the book we were looking for!'); + } +} + +// using Array.from() method +// @ts-ignore +const numbers = [1, 2, 3, 4, 5]; +const numbersCopy = Array.from(numbers); +console.log(numbersCopy); // [1, 2, 3, 4, 5] + +const evens = Array.from(numbers, x => (x % 2 == 0)); +console.log(evens); // [false, true, false, true, false] + +// Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; +const friendsCopy = Array.from(friends); +console.log(friendsCopy); + +friends[0].name = 'Sam'; +console.log(friendsCopy[0].name); // Sam + +// deep copy +const friendsDeepCopy = JSON.parse(JSON.stringify(friends)); +friends[0].name = 'Frodo'; +console.log(friendsDeepCopy[0].name); // Sam + +// using Array.of() method +const numbersArray = Array.of(1, 2, 3, 4, 5); +console.log(numbersArray); // [1, 2, 3, 4, 5] + +let numbersCopy2 = Array.of(...numbersArray); + +// using Array.fill() method +const tornamentResults = new Array(5).fill('pending'); + +tornamentResults.fill('win', 1, 3); +console.log(tornamentResults); // ['pending', 'win', 'win', 'pending', 'pending'] + +// joining arrays +const zero = 0; +const positiveNumbers = [1, 2, 3]; +const negativeNumbers = [-3, -2, -1]; +let allNumbers = negativeNumbers.concat(zero, positiveNumbers); + + +// to see the output of this file use the command: node src/03-array/06-other-methods.js \ No newline at end of file diff --git a/src/03-array/06-other-methods.ts b/src/03-array/06-other-methods.ts new file mode 100644 index 00000000..7ec3fcec --- /dev/null +++ b/src/03-array/06-other-methods.ts @@ -0,0 +1,74 @@ +// Path: src/03-array/06-other-methods.ts + +// using Array.isArray() method +console.log(typeof 'Learning Data Structures'); // string +console.log(typeof 123); // number +console.log(typeof { id: 1 }); // object +console.log(typeof [1, 2, 3]); // object + +console.log(Array.isArray([1, 2, 3])); // true + +// real world example +const jsonString = JSON.stringify('[{"id":1,"title":"The Fellowship of the Ring"},{"id":2,"title":"Fourth Wing"}]'); +const dataReceived = JSON.parse(jsonString); + +if (Array.isArray(dataReceived)) { + console.log('It is an array'); + // check if The Fellowship of the Ring is in the array + const fellowship = dataReceived.find((item) => { + return item.title === 'The Fellowship of the Ring'; + }); + if (fellowship) { + console.log('We received the book we were looking for!'); + } else { + console.log('We did not receive the book we were looking for!'); + } +} + +// using Array.from() method +// @ts-ignore +const numbers = [1, 2, 3, 4, 5]; +const numbersCopy = Array.from(numbers); +console.log(numbersCopy); // [1, 2, 3, 4, 5] + +const evens = Array.from(numbers, x => (x % 2 == 0)); +console.log(evens); // [false, true, false, true, false] + +// Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; +const friendsCopy = Array.from(friends); +console.log(friendsCopy); + +friends[0].name = 'Sam'; +console.log(friendsCopy[0].name); // Sam + +// deep copy +const friendsDeepCopy = JSON.parse(JSON.stringify(friends)); +friends[0].name = 'Frodo'; +console.log(friendsDeepCopy[0].name); // Sam + +// using Array.of() method +const numbersArray = Array.of(1, 2, 3, 4, 5); +console.log(numbersArray); // [1, 2, 3, 4, 5] + +let numbersCopy2 = Array.of(...numbersArray); + +// using Array.fill() method +const tornamentResults = new Array(5).fill('pending'); + +tornamentResults.fill('win', 1, 3); +console.log(tornamentResults); // ['pending', 'win', 'win', 'pending', 'pending'] + +// joining arrays +const zero = 0; +const positiveNumbers = [1, 2, 3]; +const negativeNumbers = [-3, -2, -1]; +let allNumbers = negativeNumbers.concat(zero, positiveNumbers); + + +// to see the output of this file use the command: node src/03-array/06-other-methods.ts \ No newline at end of file diff --git a/src/03-array/07-multidimensional-arrays.js b/src/03-array/07-multidimensional-arrays.js new file mode 100644 index 00000000..7f28d754 --- /dev/null +++ b/src/03-array/07-multidimensional-arrays.js @@ -0,0 +1,87 @@ +// Path: src/03-array/07-multidimensional-arrays.js + +let averageTempDay1 = [72, 75, 79, 79, 81, 81]; +let averageTempDay2 = [81, 79, 75, 75, 73, 72]; + +// multidimensional array representation +let averageTempMultipleDays = []; +averageTempMultipleDays[0] = [72, 75, 79, 79, 81, 81]; +averageTempMultipleDays[1] = [81, 79, 75, 75, 73, 73]; + +// same as above: +averageTempMultipleDays = [ + [72, 75, 79, 79, 81, 81], + [81, 79, 75, 75, 73, 73] +]; + +// day 1 +averageTempMultipleDays[0] = []; +averageTempMultipleDays[0][0] = 72; +averageTempMultipleDays[0][1] = 75; +averageTempMultipleDays[0][2] = 79; +averageTempMultipleDays[0][3] = 79; +averageTempMultipleDays[0][4] = 81; +averageTempMultipleDays[0][5] = 81; +// day 2 +averageTempMultipleDays[1] = []; +averageTempMultipleDays[1][0] = 81; +averageTempMultipleDays[1][1] = 79; +averageTempMultipleDays[1][2] = 75; +averageTempMultipleDays[1][3] = 75; +averageTempMultipleDays[1][4] = 73; +averageTempMultipleDays[1][5] = 73; + +// @ts-ignore +function printMultidimensionalArray(myArray) { + for (let i = 0; i < myArray.length; i++) { + for (let j = 0; j < myArray[i].length; j++) { + console.log(myArray[i][j]); + } + } +} + +printMultidimensionalArray(averageTempMultipleDays); + +console.table(averageTempMultipleDays); + +//* * Multidimensional Matrix +// Dimension 1 (i): each day +// Dimension 2 (j): location +// Dimension 3 (z): temperature +// declare a 3-dimensional Array 3x3x3: +let averageTempMultipleDaysAndLocation = []; + +// day 1 +averageTempMultipleDaysAndLocation[0] = []; +averageTempMultipleDaysAndLocation[0][0] = [19, 20, 21]; // location 1 +averageTempMultipleDaysAndLocation[0][1] = [20, 22, 23]; // location 2 +averageTempMultipleDaysAndLocation[0][2] = [30, 31, 32]; // location 3 + +// day 2 +averageTempMultipleDaysAndLocation[1] = []; +averageTempMultipleDaysAndLocation[1][0] = [21, 22, 23]; // location 1 +averageTempMultipleDaysAndLocation[1][1] = [22, 23, 24]; // location 2 +averageTempMultipleDaysAndLocation[1][2] = [29, 30, 30]; // location 3 + +// day 3 +averageTempMultipleDaysAndLocation[2] = []; +averageTempMultipleDaysAndLocation[2][0] = [22, 23, 24]; // location 1 +averageTempMultipleDaysAndLocation[2][1] = [23, 24, 23]; // location 2 +averageTempMultipleDaysAndLocation[2][2] = [30, 31, 31]; // location 3 + + +function printMultidimensionalArray3D(myArray) { + for (let i = 0; i < myArray.length; i++) { + for (let j = 0; j < myArray[i].length; j++) { + for (let z = 0; z < myArray[i][j].length; z++) { + console.log(myArray[i][j][z]); + } + } + } +} + +printMultidimensionalArray3D(averageTempMultipleDaysAndLocation); + +console.table(averageTempMultipleDaysAndLocation); + +// to see the output of this file use the command: node src/03-array/07-multidimensional-arrays.js \ No newline at end of file diff --git a/src/03-array/07-multidimensional-arrays.ts b/src/03-array/07-multidimensional-arrays.ts new file mode 100644 index 00000000..35879e40 --- /dev/null +++ b/src/03-array/07-multidimensional-arrays.ts @@ -0,0 +1,87 @@ +// Path: src/03-array/07-multidimensional-arrays.ts + +let averageTempDay1 = [72, 75, 79, 79, 81, 81]; +let averageTempDay2 = [81, 79, 75, 75, 73, 72]; + +// multidimensional array representation +let averageTempMultipleDays = []; +averageTempMultipleDays[0] = [72, 75, 79, 79, 81, 81]; +averageTempMultipleDays[1] = [81, 79, 75, 75, 73, 73]; + +// same as above: +averageTempMultipleDays = [ + [72, 75, 79, 79, 81, 81], + [81, 79, 75, 75, 73, 73] +]; + +// day 1 +averageTempMultipleDays[0] = []; +averageTempMultipleDays[0][0] = 72; +averageTempMultipleDays[0][1] = 75; +averageTempMultipleDays[0][2] = 79; +averageTempMultipleDays[0][3] = 79; +averageTempMultipleDays[0][4] = 81; +averageTempMultipleDays[0][5] = 81; +// day 2 +averageTempMultipleDays[1] = []; +averageTempMultipleDays[1][0] = 81; +averageTempMultipleDays[1][1] = 79; +averageTempMultipleDays[1][2] = 75; +averageTempMultipleDays[1][3] = 75; +averageTempMultipleDays[1][4] = 73; +averageTempMultipleDays[1][5] = 73; + +// @ts-ignore +function printMultidimensionalArray(myArray) { + for (let i = 0; i < myArray.length; i++) { + for (let j = 0; j < myArray[i].length; j++) { + console.log(myArray[i][j]); + } + } +} + +printMultidimensionalArray(averageTempMultipleDays); + +console.table(averageTempMultipleDays); + +//* * Multidimensional Matrix +// Dimension 1 (i): each day +// Dimension 2 (j): location +// Dimension 3 (z): temperature +// declare a 3-dimensional Array 3x3x3: +let averageTempMultipleDaysAndLocation = []; + +// day 1 +averageTempMultipleDaysAndLocation[0] = []; +averageTempMultipleDaysAndLocation[0][0] = [19, 20, 21]; // location 1 +averageTempMultipleDaysAndLocation[0][1] = [20, 22, 23]; // location 2 +averageTempMultipleDaysAndLocation[0][2] = [30, 31, 32]; // location 3 + +// day 2 +averageTempMultipleDaysAndLocation[1] = []; +averageTempMultipleDaysAndLocation[1][0] = [21, 22, 23]; // location 1 +averageTempMultipleDaysAndLocation[1][1] = [22, 23, 24]; // location 2 +averageTempMultipleDaysAndLocation[1][2] = [29, 30, 30]; // location 3 + +// day 3 +averageTempMultipleDaysAndLocation[2] = []; +averageTempMultipleDaysAndLocation[2][0] = [22, 23, 24]; // location 1 +averageTempMultipleDaysAndLocation[2][1] = [23, 24, 23]; // location 2 +averageTempMultipleDaysAndLocation[2][2] = [30, 31, 31]; // location 3 + + +function printMultidimensionalArray3D(myArray) { + for (let i = 0; i < myArray.length; i++) { + for (let j = 0; j < myArray[i].length; j++) { + for (let z = 0; z < myArray[i][j].length; z++) { + console.log(myArray[i][j][z]); + } + } + } +} + +printMultidimensionalArray3D(averageTempMultipleDaysAndLocation); + +console.table(averageTempMultipleDaysAndLocation); + +// to see the output of this file use the command: node src/03-array/07-multidimensional-arrays.ts \ No newline at end of file diff --git a/src/03-array/08-TypedArrays.js b/src/03-array/08-TypedArrays.js new file mode 100644 index 00000000..13e29a8a --- /dev/null +++ b/src/03-array/08-TypedArrays.js @@ -0,0 +1,15 @@ +// Path: src/03-array/08-TypedArrays.js + +// TypedArray example +const arrayLength = 5; +const int16 = new Int16Array(arrayLength); + +for (let i = 0; i < arrayLength; i++) { + int16[i] = i + 1; +} + +console.log(int16); + +// check https://js.tensorflow.org for more practical examples + +// to see the output of this file use the command: node src/03-array/08-TypedArrays.js \ No newline at end of file diff --git a/src/03-array/08-TypedArrays.ts b/src/03-array/08-TypedArrays.ts new file mode 100644 index 00000000..41a6cdc6 --- /dev/null +++ b/src/03-array/08-TypedArrays.ts @@ -0,0 +1,15 @@ +// Path: src/03-array/08-TypedArrays.ts + +// TypedArray example +const arrayLength = 5; +const int16 = new Int16Array(arrayLength); + +for (let i = 0; i < arrayLength; i++) { + int16[i] = i + 1; +} + +console.log(int16); + +// check https://js.tensorflow.org for more practical examples + +// to see the output of this file use the command: node src/03-array/08-TypedArrays.ts \ No newline at end of file diff --git a/src/03-array/09-arrays-typescript.js b/src/03-array/09-arrays-typescript.js new file mode 100644 index 00000000..d5229b24 --- /dev/null +++ b/src/03-array/09-arrays-typescript.js @@ -0,0 +1,14 @@ +// Path: src/03-array/09-arrays-typescript.js +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; +// @ts-ignore +const compareFriends = (friendA, friendB) => { + return friendA.age - friendB.age; +}; +friends.sort(compareFriends); +console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] +// to see the output of this file use the command: node src/03-array/09-arrays-typescript.js diff --git a/src/03-array/09-arrays-typescript.ts b/src/03-array/09-arrays-typescript.ts new file mode 100644 index 00000000..34cede53 --- /dev/null +++ b/src/03-array/09-arrays-typescript.ts @@ -0,0 +1,23 @@ +// Path: src/03-array/09-arrays-typescript.ts + +interface Friend { + name: string; + age: number; +} + +// @ts-ignore +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; + +// @ts-ignore +const compareFriends = (friendA: Friend, friendB: Friend) => { + return friendA.age - friendB.age; +}; +friends.sort(compareFriends); +console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] + + +// to see the output of this file use the command: node src/03-array/09-arrays-typescript.ts \ No newline at end of file diff --git a/src/03-array/10-todo-list-example.html b/src/03-array/10-todo-list-example.html new file mode 100644 index 00000000..17886bb5 --- /dev/null +++ b/src/03-array/10-todo-list-example.html @@ -0,0 +1,54 @@ + + + + + Simple TODO List (Array-Based) + + +

My TODO List

+ + + + +
    + + + + \ No newline at end of file diff --git a/src/03-array/hackerrank/01-arrays-ds.js b/src/03-array/hackerrank/01-arrays-ds.js new file mode 100644 index 00000000..55348a14 --- /dev/null +++ b/src/03-array/hackerrank/01-arrays-ds.js @@ -0,0 +1,31 @@ +// Path: src/03-array/hackerrank/01-arrays-ds.js +// Problem: https://www.hackerrank.com/challenges/arrays-ds/problem +// Solution 1 +function reverseArray(a) { + return a.reverse(); +} +// Solution 2 +function reverseArray2(a) { + const result = []; + for (let i = a.length - 1; i >= 0; i--) { + result.push(a[i]); + } + return result; +} +// Solution 3 +function reverseArray3(a) { + return a.map((_, i) => a[a.length - 1 - i]); +} +// Solution 4 +function reverseArray4(a) { + const result = []; + for (let i = 0; i < a.length; i++) { + result.unshift(a[i]); + } + return result; +} +// Test cases +console.log(reverseArray([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray2([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray3([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray4([1, 4, 3, 2])); // [2, 3, 4, 1] diff --git a/src/03-array/hackerrank/01-arrays-ds.ts b/src/03-array/hackerrank/01-arrays-ds.ts new file mode 100644 index 00000000..e0ae8044 --- /dev/null +++ b/src/03-array/hackerrank/01-arrays-ds.ts @@ -0,0 +1,47 @@ +// Path: src/03-array/hackerrank/01-arrays-ds.ts +// Problem: https://www.hackerrank.com/challenges/arrays-ds/problem + +// Solution 1 +function reverseArray(a: number[]): number[] { + return a.reverse(); +} + +// Solution 2 +function reverseArray2(a: number[]): number[] { + const result = []; + for (let i = a.length - 1; i >= 0; i--) { + result.push(a[i]); + } + return result; +} + +// Solution 3 +function reverseArray3(a: number[]): number[] { + return a.map((_, i) => a[a.length - 1 - i]); +} + +// Solution 4 +function reverseArray4(a: number[]): number[] { + const result = []; + for (let i = 0; i < a.length; i++) { + result.unshift(a[i]); + } + return result; +} + +// Solution 5, swap +function reverseArray5(a: number[]): number[] { + for (let i = 0; i < a.length / 2; i++) { + const temp = a[i]; + a[i] = a[a.length - 1 - i]; + a[a.length - 1 - i] = temp; + } + return a; +} + +// Test cases +console.log(reverseArray([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray2([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray3([1, 4, 3, 2])); // [2, 3, 4, 1] +console.log(reverseArray4([1, 4, 3, 2])); // [2, 3, 4, 1] + diff --git a/src/03-array/hackerrank/02-array-left-rotation.js b/src/03-array/hackerrank/02-array-left-rotation.js new file mode 100644 index 00000000..34a5bdb0 --- /dev/null +++ b/src/03-array/hackerrank/02-array-left-rotation.js @@ -0,0 +1,13 @@ +// Path: src/03-array/hackerrank/02-array-left-rotation.js +// Problem: https://www.hackerrank.com/challenges/array-left-rotation/problem +// Solution 1 +function rotLeft(a, d) { + return a.concat(a.splice(0, d)); +} +// Solution 2 +function rotLeft2(a, d) { + return [...a.slice(d), ...a.slice(0, d)]; +} +// Test cases +console.log(rotLeft([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] +console.log(rotLeft2([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] diff --git a/src/03-array/hackerrank/02-array-left-rotation.ts b/src/03-array/hackerrank/02-array-left-rotation.ts new file mode 100644 index 00000000..f171baaf --- /dev/null +++ b/src/03-array/hackerrank/02-array-left-rotation.ts @@ -0,0 +1,29 @@ +// Path: src/03-array/hackerrank/02-array-left-rotation.ts +// Problem: https://www.hackerrank.com/challenges/array-left-rotation/problem + +// Solution 1 +function rotLeft(a: number[], d: number): number[] { + return a.concat(a.splice(0, d)); +} + +// Solution 2 +function rotLeft2(a: number[], d: number): number[] { + return [...a.slice(d), ...a.slice(0, d)]; +} + +// Solution 3, swap the elements d times +function rotLeft3(a: number[], d: number): number[] { + for (let i = 0; i < d; i++) { + const temp = a[0]; + for (let j = 0; j < a.length - 1; j++) { + a[j] = a[j + 1]; + } + a[a.length - 1] = temp; + } + return a; +} + + +// Test cases +console.log(rotLeft([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] +console.log(rotLeft2([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] diff --git a/src/04-stack/leetcode/min-stack.ts b/src/04-stack/leetcode/min-stack.ts new file mode 100644 index 00000000..eb598f0c --- /dev/null +++ b/src/04-stack/leetcode/min-stack.ts @@ -0,0 +1,49 @@ +// Path: src/04-stack/leetcode/min-stack.ts +// https://leetcode.com/problems/min-stack/description/ + +class MinStack { + stack: number[]; + minStack: number[]; + + constructor() { + this.stack = []; + this.minStack = []; + } + + push(x: number) { + this.stack.push(x); + if (this.minStack.length === 0 || x <= this.minStack[this.minStack.length - 1]) { + this.minStack.push(x); + } + } + + pop() { + const x = this.stack.pop(); + if (x === this.minStack[this.minStack.length - 1]) { + this.minStack.pop(); + } + } + + top() { + return this.stack[this.stack.length - 1]; + } + + getMin() { + return this.minStack[this.minStack.length - 1]; + } +} + +// test +const minStack = new MinStack(); +minStack.push(-2); +minStack.push(0); +minStack.push(-3); +console.log(minStack.getMin()); // -3 +minStack.pop(); +console.log(minStack.top()); // 0 +console.log(minStack.getMin()); // -2 + +// time complexity: O(1) +// space complexity: O(n) + +// to see the output of this file use the command: node src/04-stack/leetcode/min-stack.ts \ No newline at end of file diff --git a/src/04-stack/leetcode/simplify-path.ts b/src/04-stack/leetcode/simplify-path.ts new file mode 100644 index 00000000..b7228b89 --- /dev/null +++ b/src/04-stack/leetcode/simplify-path.ts @@ -0,0 +1,43 @@ +// Path: src/04-stack/leetcode/simplify-path.ts +// https://leetcode.com/problems/simplify-path/description/ + +/** + * @param {string} path + * @return {string} + */ +const simplifyPath = function(path) { + const stack = []; + const paths = path.split('/'); + + for (let i = 0; i < paths.length; i++) { + if (paths[i] === '' || paths[i] === '.') { + continue; + } else if (paths[i] === '..') { + stack.pop(); + } else { + stack.push(paths[i]); + } + } + return '/' + stack.join('/'); +} + +// test +console.log(simplifyPath('/home/')); // '/home' +console.log(simplifyPath('/../')); // '/' +console.log(simplifyPath('/home//foo/')); // '/home/foo' +console.log(simplifyPath('/a/./b/../../c/')); // '/c' +console.log(simplifyPath('/a/../../b/../c//.//')); // '/c' +console.log(simplifyPath('/a//b////c/d//././/..')); // '/a/b/c' + +// additional tests +console.log(simplifyPath('/home/user/Documents/../Pictures')); // '/home/user/Pictures' +console.log(simplifyPath('/../home/user/Documents')); // '/home/user/Documents' +console.log(simplifyPath('/home/user/../../usr/local/bin')); // '/usr/local/bin' +console.log(simplifyPath('/home/user/./Downloads/../Pictures/././')); // '/home/user/Pictures' +console.log(simplifyPath('/home/user/Documents/../../usr/local/bin')); // '/usr/local/bin' +console.log(simplifyPath('/home/user/Documents/../../../usr/local/bin')); // '/usr/local/bin' + +// time complexity: O(n) +// space complexity: O(n) + +// to see the output of this file use the command: node src/04-stack/leetcode/simplify-path.ts \ No newline at end of file diff --git a/src/04-stack/leetcode/valid-parentheses.ts b/src/04-stack/leetcode/valid-parentheses.ts new file mode 100644 index 00000000..d3eb9f79 --- /dev/null +++ b/src/04-stack/leetcode/valid-parentheses.ts @@ -0,0 +1,64 @@ +// Path: src/04-stack/leetcode/valid-parentheses.ts +// https://leetcode.com/problems/valid-parentheses/description/ + +/** + * @param {string} s + * @return {boolean} + */ +const isValid = function(s) { + const stack = []; + const open = ['(', '[', '{']; + const close = [')', ']', '}']; + + for (let i = 0; i < s.length; i++) { + if (open.includes(s[i])) { + stack.push(s[i]); + } else if (close.includes(s[i])) { + const last = stack.pop(); + if (open.indexOf(last) !== close.indexOf(s[i])) { + return false; + } + } + } + return stack.length === 0; +} + +// test +console.log(isValid('()')); // true +console.log(isValid('()[]{}')); // true +console.log(isValid('(]')); // false +console.log(isValid('(')); // false +console.log(isValid(')')); // false + +// time complexity: O(n) +// space complexity: O(n) + +// rewrite the code using a map +const isValid2 = function(s) { + const stack = []; + const map = { + '(': ')', + '[': ']', + '{': '}' + }; + + for (let i = 0; i < s.length; i++) { + if (map[s[i]]) { + stack.push(s[i]); + } else if (s[i] !== map[stack.pop()]) { + return false; + } + } + return stack.length === 0; +} + +// test +console.log(isValid2('()')); // true +console.log(isValid2('()[]{}')); // true +console.log(isValid2('(]')); // false +console.log(isValid2('(')); // false + +// time complexity: O(n) +// space complexity: O(n) + +// to see the output of this file use the command: node src/04-stack/leetcode/valid-parentheses.ts \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..f13da160 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + //"outDir": "dist", + "rootDir": "src", + // "strict": true, + "esModuleInterop": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file From 6592091c9e7e0e3fdbfdf898c5113e03f6e44506 Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 22 May 2024 20:58:18 -0400 Subject: [PATCH 17/43] Update book title to include edition number --- README.md | 10 ++++++++-- tsconfig.json | 9 ++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a908fdb6..74f09434 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Learning JavaScript Data Structures and Algorithms =================================================== -Source code of **Learning JavaScript Data Structures and Algorithms** book. +Source code of **Learning JavaScript Data Structures and Algorithms** (4th edition) book. # Chapters @@ -58,7 +58,13 @@ npm test ``` cd src/01-intro -node 01-hello-world.js +node 01-hello-variables.js +``` + +or: + +``` +node src/01-intro/01-hello-variables.js ``` ## πŸ’» Tecnologies diff --git a/tsconfig.json b/tsconfig.json index f13da160..4771fdd1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,14 @@ { "compilerOptions": { "target": "ESNext", - "module": "ESNext", + "module": "CommonJS", //"outDir": "dist", "rootDir": "src", // "strict": true, - "esModuleInterop": true + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, }, - "include": [ - "src/**/*.ts" - ], + "include": ["src/**/*"], "exclude": [ "node_modules" ] From 1fc5e955d43652b9b2a7a8dd1192292fe7614001 Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 22 May 2024 21:03:06 -0400 Subject: [PATCH 18/43] chapter 4: stacks --- src/04-stack/01-using-stack-class.js | 34 +++++++++ src/04-stack/01-using-stack-class.ts | 44 ++++++++++++ src/04-stack/02-using-stack-object-class.js | 44 ++++++++++++ src/04-stack/02-using-stack-object-class.ts | 48 +++++++++++++ src/04-stack/03-base-converter-examples.js | 24 +++++++ src/04-stack/decimal-to-base.js | 77 +++++++++++++++++++++ src/04-stack/decimal-to-binary.js | 31 +++++++++ src/04-stack/leetcode/min-stack.ts | 58 ++++++++++++++++ src/04-stack/leetcode/valid-parentheses.ts | 29 +++++++- src/04-stack/stack-object.js | 74 ++++++++++++++++++++ src/04-stack/stack-object.ts | 72 +++++++++++++++++++ src/04-stack/stack.js | 75 ++++++++++++++++++++ src/04-stack/stack.ts | 45 ++++++++++++ src/04-stack/tower-of-hanoi.js | 32 +++++++++ 14 files changed, 686 insertions(+), 1 deletion(-) create mode 100644 src/04-stack/01-using-stack-class.js create mode 100644 src/04-stack/01-using-stack-class.ts create mode 100644 src/04-stack/02-using-stack-object-class.js create mode 100644 src/04-stack/02-using-stack-object-class.ts create mode 100644 src/04-stack/03-base-converter-examples.js create mode 100644 src/04-stack/decimal-to-base.js create mode 100644 src/04-stack/decimal-to-binary.js create mode 100644 src/04-stack/stack-object.js create mode 100644 src/04-stack/stack-object.ts create mode 100644 src/04-stack/stack.js create mode 100644 src/04-stack/stack.ts create mode 100644 src/04-stack/tower-of-hanoi.js diff --git a/src/04-stack/01-using-stack-class.js b/src/04-stack/01-using-stack-class.js new file mode 100644 index 00000000..a088d46e --- /dev/null +++ b/src/04-stack/01-using-stack-class.js @@ -0,0 +1,34 @@ +// src/04-stack/01-using-stack-class.js + +const Stack = require('./stack'); +// import { Stack } from './stack'; // or './stack.js' if you are using ES modules + +const stack = new Stack(); + +console.log(stack.isEmpty()); // true + +stack.push({action: 'typing', text: 'S'}); +stack.push({action: 'typing', text: 't'}); + +console.log(stack.peek()); // { action: 'typing', text: 't' } + +console.log(stack.size); // 2 + +stack.push({action: 'typing', text: 'a'}); +stack.push({action: 'typing', text: 'c'}); +stack.push({action: 'typing', text: 'k'}); + +console.log(stack.size); // 5 +console.log(stack.isEmpty()); // false + +// removing two elements from the stack +stack.pop(); +stack.pop(); + +console.log(stack.size); // 3 +console.log(stack.peek()); // { action: 'typing', text: 'a' } + +// toString +console.log(stack.toString()); + +// to see the output of this file use the command: node src/04-stack/01-using-stack-class.js \ No newline at end of file diff --git a/src/04-stack/01-using-stack-class.ts b/src/04-stack/01-using-stack-class.ts new file mode 100644 index 00000000..0f6d58ae --- /dev/null +++ b/src/04-stack/01-using-stack-class.ts @@ -0,0 +1,44 @@ +// src/04-stack/01-using-stack-class.ts + +import Stack from './stack'; + +enum Action { + TYPE = 'typing' +} + +interface EditorAction { + action: Action; + text: string; +} + +const stack = new Stack(); + +console.log(stack.isEmpty()); // true + +console.log(stack.peek()); // undefined + +stack.push({action: Action.TYPE, text: 'S'}); +stack.push({action: Action.TYPE, text: 't'}); + +console.log(stack.peek()); // { action: 'typing', text: 't' } + +console.log(stack.size); // 2 + +stack.push({action: Action.TYPE, text: 'a'}); +stack.push({action: Action.TYPE, text: 'c'}); +stack.push({action: Action.TYPE, text: 'k'}); + +console.log(stack.size); // 5 +console.log(stack.isEmpty()); // false + +// removing two elements from the stack +stack.pop(); +stack.pop(); + +console.log(stack.size); // 3 +console.log(stack.peek()); // { action: 'typing', text: 'a' } + +// toString +console.log(stack.toString()); + +// to see the output of this file use the command: npx ts-node src/04-stack/01-using-stack-class.ts \ No newline at end of file diff --git a/src/04-stack/02-using-stack-object-class.js b/src/04-stack/02-using-stack-object-class.js new file mode 100644 index 00000000..11e57dd3 --- /dev/null +++ b/src/04-stack/02-using-stack-object-class.js @@ -0,0 +1,44 @@ +// src/04-stack/02-using-stack-object-class.js + +const Stack = require('./stack-object'); +// import { Stack } from './stack-object'; // or './stack-object.js' if you are using ES modules + +const stack = new Stack(); + +console.log(stack.isEmpty()); // true + +console.log(stack.peek()); // undefined + +stack.push({action: 'typing', text: 'S'}); +stack.push({action: 'typing', text: 't'}); + +// internal object representation +// #items = { +// 0: { action: 'typing', text: 'S' }, +// 1: { action: 'typing', text: 't' }} +// }; +// #count = 2; + +console.log(stack.peek()); // { action: 'typing', text: 't' } + +console.log(stack.size); // 2 + +stack.push({action: 'typing', text: 'a'}); +stack.push({action: 'typing', text: 'c'}); +stack.push({action: 'typing', text: 'k'}); + +console.log(stack.size); // 5 +console.log(stack.isEmpty()); // false + +// removing two elements from the stack +stack.pop(); +stack.pop(); + +console.log(stack.size); // 3 +console.log(stack.peek()); // { action: 'typing', text: 'a' } + +// toString +console.log(stack); + + +// to see the output of this file use the command: node src/04-stack/02-using-stack-object-class.js \ No newline at end of file diff --git a/src/04-stack/02-using-stack-object-class.ts b/src/04-stack/02-using-stack-object-class.ts new file mode 100644 index 00000000..3b5cf53a --- /dev/null +++ b/src/04-stack/02-using-stack-object-class.ts @@ -0,0 +1,48 @@ +// src/04-stack/02-using-stack-object-class.ts + +import Stack from './stack-object'; + +interface EditorAction { + action: string; + text: string; +} + +const stack = new Stack(); + +console.log(stack.isEmpty()); // true + +console.log(stack.peek()); // undefined + +stack.push({action: 'typing', text: 'S'}); +stack.push({action: 'typing', text: 't'}); + +// internal object representation +// #items = { +// 0: { action: 'typing', text: 'S' }, +// 1: { action: 'typing', text: 't' }} +// }; +// #count = 2; + +console.log(stack.peek()); // { action: 'typing', text: 't' } + +console.log(stack.size); // 2 + +stack.push({action: 'typing', text: 'a'}); +stack.push({action: 'typing', text: 'c'}); +stack.push({action: 'typing', text: 'k'}); + +console.log(stack.size); // 5 +console.log(stack.isEmpty()); // false + +// removing two elements from the stack +stack.pop(); +stack.pop(); + +console.log(stack.size); // 3 +console.log(stack.peek()); // { action: 'typing', text: 'a' } + +// toString +console.log(stack); + + +// to see the output of this file use the command: npx ts-node src/04-stack/02-using-stack-object-class.js \ No newline at end of file diff --git a/src/04-stack/03-base-converter-examples.js b/src/04-stack/03-base-converter-examples.js new file mode 100644 index 00000000..1145a408 --- /dev/null +++ b/src/04-stack/03-base-converter-examples.js @@ -0,0 +1,24 @@ +// src/04-stack/03-base-converter-examples.js + +const decimalToBinary = require('./decimal-to-binary'); +const converters = require('./decimal-to-base'); + +// Decimal to binary +console.log(decimalToBinary(0)); // 0 +console.log(decimalToBinary(1)); // 1 +console.log(decimalToBinary(2)); // 10 +console.log(decimalToBinary(13)); // 1101 +console.log(decimalToBinary(233)); // 11101001 +console.log(decimalToBinary(10)); // 1010 +console.log(decimalToBinary(1000)); // 1111101000 + +// Decimal to base +console.log(converters.decimalToBase(100345, 2)); // 11000011111111001 +console.log(converters.decimalToBase(100345, 8)); // 303771 +console.log(converters.decimalToBase(100345, 16)); // 187F9 +console.log(converters.decimalToBase(100345, 35)); // 2BW0 + +// Decimal to base 64 +console.log(converters.decimalToBase64(100345, 64)); // Yf5= + +// to see the output of this file use the command: node src/04-stack/03-base-converter-examples.js \ No newline at end of file diff --git a/src/04-stack/decimal-to-base.js b/src/04-stack/decimal-to-base.js new file mode 100644 index 00000000..238e1e23 --- /dev/null +++ b/src/04-stack/decimal-to-base.js @@ -0,0 +1,77 @@ +// src/04-stack/decimal-to-base.js + +const Stack = require('./stack'); + +/** + * Converts a decimal number to a given base + * @param {number} decimalNumber - decimal number to be converted + * @param {number} base - base to convert the decimal number to + * @returns {string} base representation of the decimal number + */ +function decimalToBase(decimalNumber, base) { + + if (base < 2 || base > 36) { + throw new Error('Base must be between 2 and 36'); + } + + const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Digits for base 36 + const remainderStack = new Stack(); + let baseString = ''; + + while (decimalNumber > 0) { + const remainder = Math.floor(decimalNumber % base); + remainderStack.push(remainder); + decimalNumber = Math.floor(decimalNumber / base); + } + + while (!remainderStack.isEmpty()) { + baseString += digits[remainderStack.pop()]; // Use digit mapping + } + + return baseString; +} + +/** + * Converts a decimal number to a given base + * @param {number} decimalNumber - decimal number to be converted (between 2 and 36 or 64) + * @param {number} base - base to convert the decimal number to + * @returns {string} base representation of the decimal number + */ +function decimalToBase64(decimalNumber, base) { + if (base < 2 || (base > 36 && base !== 64)) { + throw new Error('Base must be between 2 and 36 or 64'); + } + + const digits = base === 64 ? + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' : + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + const remainderStack = new Stack(); + let baseString = ''; + + while (decimalNumber > 0) { + const remainder = Math.floor(decimalNumber % base); + remainderStack.push(remainder); + decimalNumber = Math.floor(decimalNumber / base); + } + + while (!remainderStack.isEmpty()) { + baseString += digits[remainderStack.pop()]; + } + + // Handle padding for Base64 (if necessary) + /* Base64 encodes data in groups of 3 bytes (24 bits). + Each group is represented by four Base64 characters (6 bits per character). + If the input data length isn't divisible by 3, padding characters (=) are added to the end of the output + to ensure that the total length is a multiple of 4. + This is important for proper decoding of the Base64 string back to its original data. */ + if (base === 64) { + while (baseString.length % 4 !== 0) { + baseString += '='; + } + } + + return baseString; +} + +module.exports = { decimalToBase, decimalToBase64 }; \ No newline at end of file diff --git a/src/04-stack/decimal-to-binary.js b/src/04-stack/decimal-to-binary.js new file mode 100644 index 00000000..e1bc4616 --- /dev/null +++ b/src/04-stack/decimal-to-binary.js @@ -0,0 +1,31 @@ +// src/04-stack/decimal-to-binary.js + +const Stack = require('./stack'); + +/** + * Converts a decimal number to binary + * @param {number} decimalNumber - decimal number to be converted + * @returns {string} binary representation of the decimal number + */ +function decimalToBinary(decimalNumber) { + const remainderStack = new Stack(); + let binaryString = ''; + + if (decimalNumber === 0) { + return '0'; + } + + while (decimalNumber > 0) { // {1} + const remainder = Math.floor(decimalNumber % 2); // {2} + remainderStack.push(remainder); // {3} + decimalNumber = Math.floor(decimalNumber / 2); // {4} + } + + while (!remainderStack.isEmpty()) { // {5} + binaryString += remainderStack.pop().toString(); + } + + return binaryString; +} + +module.exports = decimalToBinary; \ No newline at end of file diff --git a/src/04-stack/leetcode/min-stack.ts b/src/04-stack/leetcode/min-stack.ts index eb598f0c..1b1d9c2f 100644 --- a/src/04-stack/leetcode/min-stack.ts +++ b/src/04-stack/leetcode/min-stack.ts @@ -46,4 +46,62 @@ console.log(minStack.getMin()); // -2 // time complexity: O(1) // space complexity: O(n) +// optimized solution +class MinStack2 { + stack: number[] = []; + min: number = +Infinity; + + push(val: number): void { + this.min = Math.min(val, this.min); + this.stack.push(val); + } + + pop(): void { + const val = this.stack.pop(); + if (this.min === val) this.min = Math.min(...this.stack); + } + + top(): number { + return this.stack[this.stack.length - 1]; + } + + getMin(): number { + return this.min; + } +} + +class MinStack3 { + private stack: number[]; + private minStack: number[]; + + constructor() { + this.stack = []; + this.minStack = []; + } + + push(val: number): void { + this.stack.push(val); + + if (this.minStack.length === 0) this.minStack.push(val); + else { + const currentMin = this.minStack[this.minStack.length - 1]; + + this.minStack.push(val < currentMin ? val : currentMin); + } + } + + pop(): void { + this.stack.pop(); + this.minStack.pop(); + } + + top(): number { + return this.stack[this.stack.length - 1]; + } + + getMin(): number { + return this.minStack[this.minStack.length - 1]; + } +} + // to see the output of this file use the command: node src/04-stack/leetcode/min-stack.ts \ No newline at end of file diff --git a/src/04-stack/leetcode/valid-parentheses.ts b/src/04-stack/leetcode/valid-parentheses.ts index d3eb9f79..8c22c6a3 100644 --- a/src/04-stack/leetcode/valid-parentheses.ts +++ b/src/04-stack/leetcode/valid-parentheses.ts @@ -21,7 +21,7 @@ const isValid = function(s) { } } return stack.length === 0; -} +} // test console.log(isValid('()')); // true @@ -61,4 +61,31 @@ console.log(isValid2('(')); // false // time complexity: O(n) // space complexity: O(n) +// optimize the code +const isValid3 = function(s) { + + // optimization 1: if the length of the string is odd, return false + if (s.length % 2 === 1) return false; + + // optimization 2: if the first character is a closing bracket, return false + if (s[0] === ')' || s[0] === ']' || s[0] === '}') return false; + + // optimization 3: if the last character is an opening bracket, return false + if (s[s.length - 1] === '(' || s[s.length - 1] === '[' || s[s.length - 1] === '{') return false; + + const stack = []; + for (let i = 0; i < s.length; i++) { + if (s[i] === '(' || s[i] === '[' || s[i] === '{') stack.push(s[i]) + else { + const top = stack.pop() + if (top === '(' && s[i] !== ')') return false + if (top === '[' && s[i] !== ']') return false + if (top === '{' && s[i] !== '}') return false + if (top === undefined) return false + } + } + + return stack.length === 0 +} + // to see the output of this file use the command: node src/04-stack/leetcode/valid-parentheses.ts \ No newline at end of file diff --git a/src/04-stack/stack-object.js b/src/04-stack/stack-object.js new file mode 100644 index 00000000..a216cda3 --- /dev/null +++ b/src/04-stack/stack-object.js @@ -0,0 +1,74 @@ +// src/04-stack/stack-object.js + +// @ts-ignore +class Stack { + + // private properties + #items = {}; // {1} + #count = 0; // {2} + + push(item) { + this.#items[this.#count] = item; + this.#count++; + } + + pop() { + if (this.isEmpty()) { // {1} + return undefined; + } + this.#count--; // {2} + const result = this.#items[this.#count]; // {3} + delete this.#items[this.#count]; // {4} + return result; // {5} + } + + /** + * + * @returns the last element in the stack or undefined if the stack is empty + */ + peek() { + return this.#items[this.#count - 1]; + } + + isEmpty() { + return this.#count === 0; + } + + get size() { + return this.#count; + } + + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + this.#items = {}; + this.#count = 0; + } + + /** + * Returns a string representation of the stack, bottom to top, without modifying the stack + */ + toString() { + if (this.isEmpty()) { + return 'Empty Stack'; + } + let objString = this.#itemToString(this.#items[0]); // {1} + for (let i = 1; i < this.#count; i++) { // {2} + objString = `${objString}, ${this.#itemToString(this.#items[i])}`; // {3} + } + return objString; + } + + #itemToString(item) { // {4} + if (typeof item === 'object' && item !== null) { + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types + } + } +} + +// export node module so it can be used in different files +// CommonJS export +module.exports = Stack; \ No newline at end of file diff --git a/src/04-stack/stack-object.ts b/src/04-stack/stack-object.ts new file mode 100644 index 00000000..47d1b963 --- /dev/null +++ b/src/04-stack/stack-object.ts @@ -0,0 +1,72 @@ +// src/04-stack/stack-object.ts + +class Stack { + + // private properties + private items = {}; // {1} + private count = 0; // {2} + + push(item: T) { + this.items[this.count] = item; + this.count++; + } + + pop(): T | undefined { + if (this.isEmpty()) { // {1} + return undefined; + } + this.count--; // {2} + const result = this.items[this.count]; // {3} + delete this.items[this.count]; // {4} + return result; // {5} + } + + /** + * + * @returns the last element in the stack or undefined if the stack is empty + */ + peek(): T | undefined { + return this.items[this.count - 1]; + } + + isEmpty() { + return this.count === 0; + } + + get size() { + return this.count; + } + + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + this.items = {}; + this.count = 0; + } + + /** + * Returns a string representation of the stack, bottom to top, without modifying the stack + */ + toString() { + if (this.isEmpty()) { + return 'Empty Stack'; + } + let objString = this.itemToString(this.items[0]); // {1} + for (let i = 1; i < this.count; i++) { // {2} + objString = `${objString}, ${this.itemToString(this.items[i])}`; // {3} + } + return objString; + } + + private itemToString(item): string { // {4} + if (typeof item === 'object' && item !== null) { + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types + } + } +} + +// export node module so it can be used in different files +export default Stack; \ No newline at end of file diff --git a/src/04-stack/stack.js b/src/04-stack/stack.js new file mode 100644 index 00000000..22677113 --- /dev/null +++ b/src/04-stack/stack.js @@ -0,0 +1,75 @@ +// src/04-stack/stack.js + +// @ts-ignore +class Stack { + + // private property + #items = []; // {1} + + /** + * Adds a new element to the top of the stack + * @param {*} item - new element to be added to the stack + */ + push(item) { + this.#items.push(item); + } + + /** + * Removes the top element from the stack and returns it. If the stack is empty, undefined is returned and the stack is not modified. + */ + pop() { + return this.#items.pop(); + } + + /** + * Returns the top element of the stack without removing it. The stack is not modified (it does not remove the element; it only returns the element for information purposes). + */ + peek() { + return this.#items[this.#items.length - 1]; + } + + /** + * Returns true if the stack does not contain any elements and false if the size of the stack is bigger than 0. + */ + isEmpty() { + return this.#items.length === 0; + } + + /** + * Returns the number of elements in the stack. It is similar to the length property of an array. + */ + get size() { + return this.#items.length; + } + + /** + * Removes all the elements from the stack + */ + clear() { + /* while (!this.isEmpty()) { + this.pop(); + } */ + this.#items = []; + } + + /** + * Returns a string representation of the stack, bottom to top, without modifying the stack + */ + toString() { + if (this.isEmpty()) { + return 'Empty Stack'; + } else { + return this.#items.map(item => { // {1} + if (typeof item === 'object' && item !== null) { // {2} + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types {3} + } + }).join(', '); // {4} + } + } +} + +// export node module so it can be used in different files +// CommonJS export +module.exports = Stack; \ No newline at end of file diff --git a/src/04-stack/stack.ts b/src/04-stack/stack.ts new file mode 100644 index 00000000..5857e3db --- /dev/null +++ b/src/04-stack/stack.ts @@ -0,0 +1,45 @@ +// src/04-stack/stack.ts + +class Stack { + private items: T[] = []; + + push(item: T): void { + this.items.push(item); + } + + pop(): T | undefined { + return this.items.pop(); + } + + peek(): T | undefined { + return this.items[this.items.length - 1]; + } + + isEmpty(): boolean { + return this.items.length === 0; + } + + get size(): number { + return this.items.length; + } + + clear(): void { + this.items = []; + } + + toString(): string { + if (this.isEmpty()) { + return 'Empty Stack'; + } else { + return this.items.map(item => { + if (typeof item === 'object' && item !== null) { + return JSON.stringify(item); + } else { + return item.toString(); + } + }).join(', '); + } + } +} + +export default Stack; \ No newline at end of file diff --git a/src/04-stack/tower-of-hanoi.js b/src/04-stack/tower-of-hanoi.js new file mode 100644 index 00000000..eb9ac046 --- /dev/null +++ b/src/04-stack/tower-of-hanoi.js @@ -0,0 +1,32 @@ +// src/04-stack/tower-of-hanoi.js + +const Stack = require('./stack'); + +/** + * Solves the Tower of Hanoi puzzle + * @param {number} n - number of disks + * @param {Stack} source - source stack + * @param {Stack} auxiliary - auxiliary stack + * @param {Stack} destination - destination stack + */ +function towerOfHanoi(n, source, auxiliary, destination) { + if (n > 0) { + towerOfHanoi(n - 1, source, destination, auxiliary); + destination.push(source.pop()); + towerOfHanoi(n - 1, auxiliary, source, destination); + } +} + + +// Usage +// const source = new Stack(); +// const auxiliary = new Stack(); +// const destination = new Stack(); +// source.push(3); +// source.push(2); +// source.push(1); +// towerOfHanoi(3, source, auxiliary, destination); +// console.log(destination); // [1, 2, 3] + + +module.exports = towerOfHanoi; From bfde8e56d3b44a0a18c772f3198579ee8c9082d9 Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 22 May 2024 21:03:06 -0400 Subject: [PATCH 19/43] chapter 4: stacks --- src/04-stack/03-base-converter-examples.ts | 24 +++++++ src/04-stack/decimal-to-base.ts | 77 ++++++++++++++++++++++ src/04-stack/decimal-to-binary.ts | 31 +++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/04-stack/03-base-converter-examples.ts create mode 100644 src/04-stack/decimal-to-base.ts create mode 100644 src/04-stack/decimal-to-binary.ts diff --git a/src/04-stack/03-base-converter-examples.ts b/src/04-stack/03-base-converter-examples.ts new file mode 100644 index 00000000..78cea812 --- /dev/null +++ b/src/04-stack/03-base-converter-examples.ts @@ -0,0 +1,24 @@ +// src/04-stack/03-base-converter-examples.ts + +import decimalToBinary from './decimal-to-binary'; +import { decimalToBase, decimalToBase64 } from './decimal-to-base'; + +// Decimal to binary +console.log(decimalToBinary(0)); // 0 +console.log(decimalToBinary(1)); // 1 +console.log(decimalToBinary(2)); // 10 +console.log(decimalToBinary(13)); // 1101 +console.log(decimalToBinary(233)); // 11101001 +console.log(decimalToBinary(10)); // 1010 +console.log(decimalToBinary(1000)); // 1111101000 + +// Decimal to base +console.log(decimalToBase(100345, 2)); // 11000011111111001 +console.log(decimalToBase(100345, 8)); // 303771 +console.log(decimalToBase(100345, 16)); // 187F9 +console.log(decimalToBase(100345, 35)); // 2BW0 + +// Decimal to base 64 +console.log(decimalToBase64(100345, 64)); // Yf5= + +// to see the output of this file use the command: npx ts-node src/04-stack/03-base-converter-examples.js \ No newline at end of file diff --git a/src/04-stack/decimal-to-base.ts b/src/04-stack/decimal-to-base.ts new file mode 100644 index 00000000..1b5e8540 --- /dev/null +++ b/src/04-stack/decimal-to-base.ts @@ -0,0 +1,77 @@ +// src/04-stack/decimal-to-base.js + +import Stack from './stack'; + +/** + * Converts a decimal number to a given base + * @param {number} decimalNumber - decimal number to be converted + * @param {number} base - base to convert the decimal number to + * @returns {string} base representation of the decimal number + */ +function decimalToBase(decimalNumber: number, base : number) { + + if (base < 2 || base > 36) { + throw new Error('Base must be between 2 and 36'); + } + + const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Digits for base 36 + const remainderStack = new Stack(); + let baseString = ''; + + while (decimalNumber > 0) { + const remainder = Math.floor(decimalNumber % base); + remainderStack.push(remainder); + decimalNumber = Math.floor(decimalNumber / base); + } + + while (!remainderStack.isEmpty()) { + baseString += digits[remainderStack.pop()]; // Use digit mapping + } + + return baseString; +} + +/** + * Converts a decimal number to a given base + * @param {number} decimalNumber - decimal number to be converted (between 2 and 36 or 64) + * @param {number} base - base to convert the decimal number to + * @returns {string} base representation of the decimal number + */ +function decimalToBase64(decimalNumber: number, base: number) { + if (base < 2 || (base > 36 && base !== 64)) { + throw new Error('Base must be between 2 and 36 or 64'); + } + + const digits = base === 64 ? + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' : + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + const remainderStack = new Stack(); + let baseString = ''; + + while (decimalNumber > 0) { + const remainder = Math.floor(decimalNumber % base); + remainderStack.push(remainder); + decimalNumber = Math.floor(decimalNumber / base); + } + + while (!remainderStack.isEmpty()) { + baseString += digits[remainderStack.pop()]; + } + + // Handle padding for Base64 (if necessary) + /* Base64 encodes data in groups of 3 bytes (24 bits). + Each group is represented by four Base64 characters (6 bits per character). + If the input data length isn't divisible by 3, padding characters (=) are added to the end of the output + to ensure that the total length is a multiple of 4. + This is important for proper decoding of the Base64 string back to its original data. */ + if (base === 64) { + while (baseString.length % 4 !== 0) { + baseString += '='; + } + } + + return baseString; +} + +export { decimalToBase, decimalToBase64 }; \ No newline at end of file diff --git a/src/04-stack/decimal-to-binary.ts b/src/04-stack/decimal-to-binary.ts new file mode 100644 index 00000000..96804489 --- /dev/null +++ b/src/04-stack/decimal-to-binary.ts @@ -0,0 +1,31 @@ +// src/04-stack/decimal-to-binary.js + +import Stack from './stack'; + +/** + * Converts a decimal number to binary + * @param {number} decimalNumber - decimal number to be converted + * @returns {string} binary representation of the decimal number + */ +function decimalToBinary(decimalNumber: number) { + const remainderStack = new Stack(); + let binaryString = ''; + + if (decimalNumber === 0) { + return '0'; + } + + while (decimalNumber > 0) { // {1} + const remainder = Math.floor(decimalNumber % 2); // {2} + remainderStack.push(remainder); // {3} + decimalNumber = Math.floor(decimalNumber / 2); // {4} + } + + while (!remainderStack.isEmpty()) { // {5} + binaryString += remainderStack.pop().toString(); + } + + return binaryString; +} + +export default decimalToBinary; \ No newline at end of file From 5a5ddad9cbcaf2d3b7976dfd26497a513eb3da2d Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 12:24:53 -0400 Subject: [PATCH 20/43] chore: Add exercises and algorithms for stacks and arrays --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 74f09434..ad176a72 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,20 @@ Source code of **Learning JavaScript Data Structures and Algorithms** (4th editi ## Part 2: Data Structures * 03: Arrays + * _Hackerrank_ exercises: + * Arrays DS + * Array Left Rotation * 04: Stacks + * Stack data structure (array-based) + * Stack data structure (object-based) + * Decimal to binary algorithm + * Decimal to bases 2-32 algorithm + * Decimal to bases 2-32 or 64 algorithm + * Tower of Hanoi algorithm + * _LeetCode_ exercises: + * Valid Parantheses + * MinStack + * Simplify Path * 05: Queues and Deques * 06: Linked Lists * 07: Sets From d9f79111e48114c958f6966774bfdbce56a7eb97 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 12:39:54 -0400 Subject: [PATCH 21/43] chore: added Jest for testing --- package-lock.json | 3473 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 2 files changed, 3475 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 0d4bee8b..175f813c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,3318 @@ "name": "javascript-datastructures-algorithms", "version": "4.0.0", "devDependencies": { + "jest": "^29.7.0", "typescript": "^5.4.5" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001621", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", + "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.779", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.779.tgz", + "integrity": "sha512-oaTiIcszNfySXVJzKcjxd2YjPxziAd+GmXyb2HbidCeFo6Z88ygOT7EimlrEQhM2U08VhSrbKhLOXP0kKUCZ6g==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", @@ -23,6 +3332,170 @@ "engines": { "node": ">=14.17" } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 1f51d4fd..9e2b57dd 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "description": "Learning JavaScript Data Structures and Algorithms", "scripts": { "build:ts": "tsc -p tsconfig.json", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "jest" }, "author": "Loiane Groner", "devDependencies": { + "jest": "^29.7.0", "typescript": "^5.4.5" } } From 3a91f721a959237b494f9fb0a02a40471ddfb1d6 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 12:42:48 -0400 Subject: [PATCH 22/43] chore: Add ts-node as a dev dependency --- jest.config.ts | 199 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 153 +++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 353 insertions(+) create mode 100644 jest.config.ts diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..e2005ab2 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,199 @@ +/** + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +import type {Config} from 'jest'; + +const config: Config = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/0n/pj12f8hx3d93_n_gvxzr7qwc0000gn/T/jest_dx", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + // testEnvironment: "jest-environment-node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; + +export default config; diff --git a/package-lock.json b/package-lock.json index 175f813c..524ec2ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "4.0.0", "devDependencies": { "jest": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.4.5" } }, @@ -577,6 +578,28 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -952,6 +975,30 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1056,6 +1103,27 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1108,6 +1176,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1464,6 +1538,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1527,6 +1607,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -2701,6 +2790,12 @@ "node": ">=10" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -3299,6 +3394,49 @@ "node": ">=8.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3369,6 +3507,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", @@ -3485,6 +3629,15 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 9e2b57dd..5debbc3e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "author": "Loiane Groner", "devDependencies": { "jest": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.4.5" } } From 7fa28519b58c4c06a8e1bdf75872659a84712924 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 13:05:22 -0400 Subject: [PATCH 23/43] chore: Update Jest configuration to use ts-jest preset and add coverage directory to .gitignore --- .gitignore | 3 +- jest.config.ts | 4 +-- package-lock.json | 79 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 40b878db..642271f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +coverage/ \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index e2005ab2..cb9f7f6b 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -102,7 +102,7 @@ const config: Config = { // notifyMode: "failure-change", // A preset that is used as a base for Jest's configuration - // preset: undefined, + preset: "ts-jest", // Run tests from one or more projects // projects: undefined, @@ -146,7 +146,7 @@ const config: Config = { // snapshotSerializers: [], // The test environment that will be used for testing - // testEnvironment: "jest-environment-node", + testEnvironment: "node", // Options that will be passed to the testEnvironment // testEnvironmentOptions: {}, diff --git a/package-lock.json b/package-lock.json index 524ec2ea..30e3acdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,9 @@ "name": "javascript-datastructures-algorithms", "version": "4.0.0", "devDependencies": { + "@jest/globals": "^29.7.0", "jest": "^29.7.0", + "ts-jest": "^29.1.3", "ts-node": "^10.9.2", "typescript": "^5.4.5" } @@ -1358,6 +1360,18 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2754,6 +2768,12 @@ "node": ">=8" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3394,6 +3414,65 @@ "node": ">=8.0" } }, + "node_modules/ts-jest": { + "version": "29.1.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.3.tgz", + "integrity": "sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", diff --git a/package.json b/package.json index 5debbc3e..3b607474 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ }, "author": "Loiane Groner", "devDependencies": { + "@jest/globals": "^29.7.0", "jest": "^29.7.0", + "ts-jest": "^29.1.3", "ts-node": "^10.9.2", "typescript": "^5.4.5" } From a4fe5928751d557c621ecd07afc95379b3112273 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 13:16:21 -0400 Subject: [PATCH 24/43] feat: add tests --- src/04-stack/_test_/decimal-to-base.test.ts | 28 ++++++ src/04-stack/_test_/decimal-to-binary.test.ts | 28 ++++++ src/04-stack/_test_/stack-object.test.ts | 97 +++++++++++++++++++ src/04-stack/_test_/stack.test.ts | 92 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 src/04-stack/_test_/decimal-to-base.test.ts create mode 100644 src/04-stack/_test_/decimal-to-binary.test.ts create mode 100644 src/04-stack/_test_/stack-object.test.ts create mode 100644 src/04-stack/_test_/stack.test.ts diff --git a/src/04-stack/_test_/decimal-to-base.test.ts b/src/04-stack/_test_/decimal-to-base.test.ts new file mode 100644 index 00000000..8c0de9f1 --- /dev/null +++ b/src/04-stack/_test_/decimal-to-base.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from '@jest/globals'; +import decimalToBinary from '../decimal-to-binary'; + +describe('decimalToBinary', () => { + test('should convert decimal number 0 to binary', () => { + expect(decimalToBinary(0)).toBe('0'); + }); + + test('should convert decimal number 1 to binary', () => { + expect(decimalToBinary(1)).toBe('1'); + }); + + test('should convert decimal number 10 to binary', () => { + expect(decimalToBinary(10)).toBe('1010'); + }); + + test('should convert decimal number 15 to binary', () => { + expect(decimalToBinary(15)).toBe('1111'); + }); + + test('should convert decimal number 100 to binary', () => { + expect(decimalToBinary(100)).toBe('1100100'); + }); + + test('should convert decimal number 255 to binary', () => { + expect(decimalToBinary(255)).toBe('11111111'); + }); +}); \ No newline at end of file diff --git a/src/04-stack/_test_/decimal-to-binary.test.ts b/src/04-stack/_test_/decimal-to-binary.test.ts new file mode 100644 index 00000000..8c0de9f1 --- /dev/null +++ b/src/04-stack/_test_/decimal-to-binary.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from '@jest/globals'; +import decimalToBinary from '../decimal-to-binary'; + +describe('decimalToBinary', () => { + test('should convert decimal number 0 to binary', () => { + expect(decimalToBinary(0)).toBe('0'); + }); + + test('should convert decimal number 1 to binary', () => { + expect(decimalToBinary(1)).toBe('1'); + }); + + test('should convert decimal number 10 to binary', () => { + expect(decimalToBinary(10)).toBe('1010'); + }); + + test('should convert decimal number 15 to binary', () => { + expect(decimalToBinary(15)).toBe('1111'); + }); + + test('should convert decimal number 100 to binary', () => { + expect(decimalToBinary(100)).toBe('1100100'); + }); + + test('should convert decimal number 255 to binary', () => { + expect(decimalToBinary(255)).toBe('11111111'); + }); +}); \ No newline at end of file diff --git a/src/04-stack/_test_/stack-object.test.ts b/src/04-stack/_test_/stack-object.test.ts new file mode 100644 index 00000000..7164881b --- /dev/null +++ b/src/04-stack/_test_/stack-object.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, test, beforeEach } from '@jest/globals'; +import Stack from '../stack-object'; + +describe('Stack', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + test('push should add an item to the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.size).toBe(3); + }); + + test('pop should remove and return the top item from the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + const poppedItem = stack.pop(); + + expect(poppedItem).toBe(3); + expect(stack.size).toBe(2); + }); + + test('pop should return undefined if the stack is empty', () => { + expect(stack.pop()).toBeUndefined(); + }); + + test('peek should return the top item from the stack without removing it', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + const topItem = stack.peek(); + + expect(topItem).toBe(3); + expect(stack.size).toBe(3); + }); + + test('isEmpty should return true if the stack is empty', () => { + expect(stack.isEmpty()).toBe(true); + + stack.push(1); + + expect(stack.isEmpty()).toBe(false); + }); + + test('size should return the number of items in the stack', () => { + expect(stack.size).toBe(0); + + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.size).toBe(3); + }); + + test('clear should remove all items from the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + stack.clear(); + + expect(stack.size).toBe(0); + expect(stack.isEmpty()).toBe(true); + }); + + test('toString should return a string representation of the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.toString()).toBe('1, 2, 3'); + }); + + test('toString should return an empty string if the stack is empty', () => { + expect(stack.toString()).toBe('Empty Stack'); + }); + + test('toString should return a string representation of the stack with custom object', () => { + const stack = new Stack<{ key: string; value: number }>(); + + stack.push({ key: 'a', value: 1 }); + stack.push({ key: 'b', value: 2 }); + stack.push({ key: 'c', value: 3 }); + + expect(stack.toString()).toBe( + '{"key":"a","value":1}, {"key":"b","value":2}, {"key":"c","value":3}' + ); + }); +}); \ No newline at end of file diff --git a/src/04-stack/_test_/stack.test.ts b/src/04-stack/_test_/stack.test.ts new file mode 100644 index 00000000..d01c3aad --- /dev/null +++ b/src/04-stack/_test_/stack.test.ts @@ -0,0 +1,92 @@ +import {describe, expect, test, beforeEach} from '@jest/globals'; +import Stack from '../stack'; + +describe('Stack', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + test('push should add an item to the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.size).toBe(3); + }); + + test('pop should remove and return the top item from the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + const poppedItem = stack.pop(); + + expect(poppedItem).toBe(3); + expect(stack.size).toBe(2); + }); + + test('peek should return the top item from the stack without removing it', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + const topItem = stack.peek(); + + expect(topItem).toBe(3); + expect(stack.size).toBe(3); + }); + + test('isEmpty should return true if the stack is empty', () => { + expect(stack.isEmpty()).toBe(true); + + stack.push(1); + + expect(stack.isEmpty()).toBe(false); + }); + + test('size should return the number of items in the stack', () => { + expect(stack.size).toBe(0); + + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.size).toBe(3); + }); + + test('clear should remove all items from the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + stack.clear(); + + expect(stack.size).toBe(0); + expect(stack.isEmpty()).toBe(true); + }); + + test('toString should return a string representation of the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.toString()).toBe('1, 2, 3'); + }); + + test('toString should return an empty string if the stack is empty', () => { + expect(stack.toString()).toBe('Empty Stack'); + }); + + test('toString should return a string representation of the stack with custom object', () => { + const stack = new Stack<{key: string, value: number}>(); + + stack.push({key: 'a', value: 1}); + stack.push({key: 'b', value: 2}); + stack.push({key: 'c', value: 3}); + + expect(stack.toString()).toBe('{"key":"a","value":1}, {"key":"b","value":2}, {"key":"c","value":3}'); + }); + +}); \ No newline at end of file From ce64ffaf4dfb8df9feea3c9dd585e484bd305a3e Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 23 May 2024 13:37:13 -0400 Subject: [PATCH 25/43] moved files to __test__ folder --- src/04-stack/{_test_ => __test__}/decimal-to-base.test.ts | 0 src/04-stack/{_test_ => __test__}/decimal-to-binary.test.ts | 0 src/04-stack/{_test_ => __test__}/stack-object.test.ts | 0 src/04-stack/{_test_ => __test__}/stack.test.ts | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/04-stack/{_test_ => __test__}/decimal-to-base.test.ts (100%) rename src/04-stack/{_test_ => __test__}/decimal-to-binary.test.ts (100%) rename src/04-stack/{_test_ => __test__}/stack-object.test.ts (100%) rename src/04-stack/{_test_ => __test__}/stack.test.ts (100%) diff --git a/src/04-stack/_test_/decimal-to-base.test.ts b/src/04-stack/__test__/decimal-to-base.test.ts similarity index 100% rename from src/04-stack/_test_/decimal-to-base.test.ts rename to src/04-stack/__test__/decimal-to-base.test.ts diff --git a/src/04-stack/_test_/decimal-to-binary.test.ts b/src/04-stack/__test__/decimal-to-binary.test.ts similarity index 100% rename from src/04-stack/_test_/decimal-to-binary.test.ts rename to src/04-stack/__test__/decimal-to-binary.test.ts diff --git a/src/04-stack/_test_/stack-object.test.ts b/src/04-stack/__test__/stack-object.test.ts similarity index 100% rename from src/04-stack/_test_/stack-object.test.ts rename to src/04-stack/__test__/stack-object.test.ts diff --git a/src/04-stack/_test_/stack.test.ts b/src/04-stack/__test__/stack.test.ts similarity index 100% rename from src/04-stack/_test_/stack.test.ts rename to src/04-stack/__test__/stack.test.ts From 2ac4fb656164a679bb57e586bb338d54c1b3e224 Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 14 Jun 2024 11:31:02 -0400 Subject: [PATCH 26/43] chapter 5: queues and deques --- src/05-queue-deque/01-using-queue-class.js | 26 +++++ .../02-using-queue-two-pointers.js | 33 +++++++ src/05-queue-deque/03-using-deque-class.js | 44 +++++++++ src/05-queue-deque/__test__/deque.test.ts | 95 +++++++++++++++++++ src/05-queue-deque/__test__/queue.test.ts | 83 ++++++++++++++++ src/05-queue-deque/deque.js | 57 +++++++++++ src/05-queue-deque/deque.ts | 69 ++++++++++++++ src/05-queue-deque/hot-potato.js | 72 ++++++++++++++ src/05-queue-deque/leetcode/ex.md | 36 +++++++ .../number-of-students-unable-to-eat-lunch.ts | 74 +++++++++++++++ .../leetcode/time-needed-to-buy-tickets.ts | 30 ++++++ src/05-queue-deque/palindrome-checker.js | 27 ++++++ src/05-queue-deque/queue.js | 46 +++++++++ src/05-queue-deque/queue.ts | 52 ++++++++++ 14 files changed, 744 insertions(+) create mode 100644 src/05-queue-deque/01-using-queue-class.js create mode 100644 src/05-queue-deque/02-using-queue-two-pointers.js create mode 100644 src/05-queue-deque/03-using-deque-class.js create mode 100644 src/05-queue-deque/__test__/deque.test.ts create mode 100644 src/05-queue-deque/__test__/queue.test.ts create mode 100644 src/05-queue-deque/deque.js create mode 100644 src/05-queue-deque/deque.ts create mode 100644 src/05-queue-deque/hot-potato.js create mode 100644 src/05-queue-deque/leetcode/ex.md create mode 100644 src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts create mode 100644 src/05-queue-deque/leetcode/time-needed-to-buy-tickets.ts create mode 100644 src/05-queue-deque/palindrome-checker.js create mode 100644 src/05-queue-deque/queue.js create mode 100644 src/05-queue-deque/queue.ts diff --git a/src/05-queue-deque/01-using-queue-class.js b/src/05-queue-deque/01-using-queue-class.js new file mode 100644 index 00000000..a921a27b --- /dev/null +++ b/src/05-queue-deque/01-using-queue-class.js @@ -0,0 +1,26 @@ +// src/05-queue-deque/01-using-queue-class.js + +const Queue = require('./queue'); + +const queue = new Queue(); + +console.log(queue.isEmpty()); // true + +queue.enqueue({ document: 'Chapter05.docx', pages: 20 }); +queue.enqueue({ document: 'JavaScript.pdf', pages: 60 }); +queue.enqueue({ document: 'TypeScript.pdf', pages: 80 }); + +console.log(queue.toString()); + +console.log(queue.size); // 3 + +console.log(queue.isEmpty()); // false + +console.log(queue.front()); // { document: 'Chapter05.docx', pages: 20 } + +// print all documents +while (!queue.isEmpty()) { + console.log(queue.dequeue()); +} + +// to see the output of this file use the command: node src/05-queue-deque/01-using-queue-class.js \ No newline at end of file diff --git a/src/05-queue-deque/02-using-queue-two-pointers.js b/src/05-queue-deque/02-using-queue-two-pointers.js new file mode 100644 index 00000000..8395a12f --- /dev/null +++ b/src/05-queue-deque/02-using-queue-two-pointers.js @@ -0,0 +1,33 @@ +// src/05-queue-deque/02-using-queue-two-pointers.js + +const Queue = require('./queue-two-pointers'); + +const queue = new Queue(); + +console.log(queue.isEmpty()); // true + +queue.enqueue({ document: 'Chapter05.docx', pages: 20 }); +queue.enqueue({ document: 'JavaScript.pdf', pages: 60 }); +queue.enqueue({ document: 'TypeScript.pdf', pages: 80 }); + +console.log(queue.toString()); + +console.log(queue.size); // 3 + +console.log(queue.isEmpty()); // false + +console.log(queue.front()); // { document: 'Chapter05.docx', pages: 20 } + +// print all documents +while (!queue.isEmpty()) { + console.log(queue.dequeue()); +} + +// add more documents +queue.enqueue({ document: 'DataStructures.pdf', pages: 400 }); + +console.log(queue.toString()); +console.log(queue.size); // 1 +console.log(queue.front()); // { document: 'DataStructures.pdf', pages: 400 } + +// to see the output of this file use the command: node src/05-queue-deque/02-using-queue-two-pointers.js \ No newline at end of file diff --git a/src/05-queue-deque/03-using-deque-class.js b/src/05-queue-deque/03-using-deque-class.js new file mode 100644 index 00000000..e78c4412 --- /dev/null +++ b/src/05-queue-deque/03-using-deque-class.js @@ -0,0 +1,44 @@ +// src/05-queue-deque/03-using-deque-class.js + +const Deque = require('./deque'); + +class BrowserHistory { + #history = new Deque(); // Stores visited pages + #currentPage = null; + + visit(url) { + this.#history.addFront(url); + this.#currentPage = url; + } + + goBack() { + if (this.#history.size() > 1) { // Check if there's a previous page + this.#history.removeFront(); // Remove the current page + this.#currentPage = this.#history.peekFront(); // Set current to the previous + } + } + + goForward() { + if (this.#currentPage !== this.#history.peekBack()) { // Check if there's a next page + this.#history.addFront(this.#currentPage); // Add the current page back + this.#currentPage = this.#history.removeFront(); // Set current to the next page + } + } + + get currentPage() { + return this.#currentPage; + } +} + +const browser = new BrowserHistory(); +browser.visit('loiane.com'); +browser.visit('https://loiane.com/about'); // click on About menu + +browser.goBack(); +console.log(browser.currentPage); // loiane.com + +browser.goForward(); +console.log(browser.currentPage); // https://loiane.com/about + + +// to see the output of this file use the command: node src/05-queue-deque/03-using-deque-class.js \ No newline at end of file diff --git a/src/05-queue-deque/__test__/deque.test.ts b/src/05-queue-deque/__test__/deque.test.ts new file mode 100644 index 00000000..5fe61ceb --- /dev/null +++ b/src/05-queue-deque/__test__/deque.test.ts @@ -0,0 +1,95 @@ +import {describe, expect, test, beforeEach} from '@jest/globals'; +import Deque from '../deque'; + +describe('Deque', () => { + let deque: Deque; + + beforeEach(() => { + deque = new Deque(); + }); + + test('should add an element to the front of the deque', () => { + deque.addFront(1); + deque.addFront(2); + expect(deque.toString()).toBe('2, 1'); + }); + + test('should add an element to the rear of the deque', () => { + deque.addRear(1); + deque.addRear(2); + expect(deque.toString()).toBe('1, 2'); + }); + + test('should remove an element from the front of the deque', () => { + deque.addFront(1); + deque.addFront(2); + deque.removeFront(); + expect(deque.toString()).toBe('1'); + }); + + test('should return undefined when removing from an empty deque', () => { + expect(deque.removeFront()).toBeUndefined(); + }); + + test('should remove an element from the rear of the deque', () => { + deque.addRear(1); + deque.addRear(2); + deque.removeRear(); + expect(deque.toString()).toBe('1'); + }); + + test('should return undefined when removing from an empty deque', () => { + expect(deque.removeRear()).toBeUndefined(); + }); + + test('should return the front element of the deque', () => { + deque.addFront(1); + deque.addFront(2); + expect(deque.peekFront()).toBe(2); + }); + + test('should return undefined when peeking from an empty deque', () => { + expect(deque.peekFront()).toBeUndefined(); + }); + + test('should return the rear element of the deque', () => { + deque.addRear(1); + deque.addRear(2); + expect(deque.peekRear()).toBe(2); + }); + + test('should return undefined when peeking from an empty deque', () => { + expect(deque.peekRear()).toBeUndefined(); + }); + + test('should return true if the deque is empty', () => { + expect(deque.isEmpty()).toBe(true); + }); + + test('should return the size of the deque', () => { + deque.addFront(1); + deque.addFront(2); + expect(deque.size()).toBe(2); + }); + + test('should clear the deque', () => { + deque.addFront(1); + deque.addFront(2); + deque.clear(); + expect(deque.isEmpty()).toBe(true); + }); + + test('should convert the deque to a string', () => { + expect(deque.toString()).toBe('Empty Deque'); + }); + + test('should convert the deque to a string with objects', () => { + const deque = new Deque<{key: string, value: number}>(); + + deque.addFront({key: 'a', value: 1}); + deque.addFront({key: 'b', value: 2}); + deque.addFront({key: 'c', value: 3}); + + expect(deque.toString()).toBe('{"key":"c","value":3}, {"key":"b","value":2}, {"key":"a","value":1}'); + }); +}); diff --git a/src/05-queue-deque/__test__/queue.test.ts b/src/05-queue-deque/__test__/queue.test.ts new file mode 100644 index 00000000..020666b7 --- /dev/null +++ b/src/05-queue-deque/__test__/queue.test.ts @@ -0,0 +1,83 @@ +import {describe, expect, test, beforeEach} from '@jest/globals'; +import Queue from '../queue'; + +describe('Queue', () => { + let queue; + + beforeEach(() => { + queue = new Queue(); + }); + + test('should enqueue and dequeue elements correctly', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + expect(queue.dequeue()).toBe(1); + expect(queue.dequeue()).toBe(2); + expect(queue.dequeue()).toBe(3); + expect(queue.isEmpty()).toBe(true); + }); + + test('should return undefined when dequeueing from an empty queue', () => { + expect(queue.dequeue()).toBeUndefined(); + }); + + test('should return the correct size after enqueueing and dequeueing elements', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + queue.dequeue(); + queue.dequeue(); + + expect(queue.size()).toBe(1); + }); + + test('should return the correct front element after enqueueing and dequeueing elements', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + queue.dequeue(); + + expect(queue.front()).toBe(2); + }); + + test('should return undefined when calling front on an empty queue', () => { + expect(queue.front()).toBeUndefined(); + }); + + test('should clear the queue and return the correct size', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + queue.clear(); + + expect(queue.isEmpty()).toBe(true); + expect(queue.size()).toBe(0); + }); + + test('should convert the queue to a string', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + + expect(queue.toString()).toBe('1, 2, 3'); + }); + + test('should convert the queue to a string with empty queue', () => { + expect(queue.toString()).toBe('Empty Queue'); + }); + + test('should convert the queue to a string with object', () => { + queue.enqueue({name: 'John', age: 30}); + queue.enqueue({name: 'Jane', age: 25}); + queue.enqueue({name: 'Doe', age: 40}); + + expect(queue.toString()).toBe( + '{"name":"John","age":30}, {"name":"Jane","age":25}, {"name":"Doe","age":40}', + ); + }); +}); diff --git a/src/05-queue-deque/deque.js b/src/05-queue-deque/deque.js new file mode 100644 index 00000000..1d2eaebd --- /dev/null +++ b/src/05-queue-deque/deque.js @@ -0,0 +1,57 @@ +// src/05-queue-deque/deque.js + +class Deque { + #items = []; + + addFront(item) { + this.#items.unshift(item); + } + + addRear(item) { + this.#items.push(item); + } + + removeFront() { + return this.#items.shift(); + } + + removeRear() { + return this.#items.pop(); + } + + peekFront() { + return this.#items[0]; + } + + peekRear() { + return this.#items[this.#items.length - 1]; + } + + isEmpty() { + return this.#items.length === 0; + } + + get size() { + return this.#items.length; + } + + clear() { + this.#items = []; + } + + toString() { + if (this.isEmpty()) { + return 'Empty Deque'; + } else { + return this.#items.map(item => { // {1} + if (typeof item === 'object' && item !== null) { // {2} + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types {3} + } + }).join(', '); // {4} + } + } +} + +module.exports = Deque; \ No newline at end of file diff --git a/src/05-queue-deque/deque.ts b/src/05-queue-deque/deque.ts new file mode 100644 index 00000000..49a4e3ba --- /dev/null +++ b/src/05-queue-deque/deque.ts @@ -0,0 +1,69 @@ +// src/05-queue-deque/deque.ts + +class Deque { + private items: T[] = []; + + addFront(item: T): void { + this.items.unshift(item); + } + + addRear(item: T): void { + this.items.push(item); + } + + removeFront(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items.shift(); + } + + removeRear(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items.pop(); + } + + peekFront(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items[0]; + } + + peekRear(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items[this.items.length - 1]; + } + + isEmpty(): boolean { + return this.items.length === 0; + } + + get size(): number { + return this.items.length; + } + + clear(): void { + this.items = []; + } + + toString(): string { + if (this.isEmpty()) { + return 'Empty Deque'; + } else { + return this.items.map(item => { // {1} + if (typeof item === 'object' && item !== null) { // {2} + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types {3} + } + }).join(', '); // {4} + } + } +} + +export default Deque; \ No newline at end of file diff --git a/src/05-queue-deque/hot-potato.js b/src/05-queue-deque/hot-potato.js new file mode 100644 index 00000000..c20ecfdc --- /dev/null +++ b/src/05-queue-deque/hot-potato.js @@ -0,0 +1,72 @@ +class CircularQueue { + #items = []; + #capacity = 0; + #front = 0; + #rear = -1; + #size = 0; + + constructor(capacity) { + this.#items = new Array(capacity); + this.#capacity = capacity; + } + + enqueue(item) { + if (this.isFull()) { + throw new Error("Queue is full"); + } + this.#rear = (this.#rear + 1) % this.#capacity; + this.#items[this.#rear] = item; + this.#size++; + } + + dequeue() { + if (this.isEmpty()) { throw new Error("Queue is empty"); } + + const item = this.#items[this.#front]; + this.#size--; + + if (this.isEmpty()) { + this.#front = 0; + this.#rear = -1; + } else { + this.#front = (this.#front + 1) % this.#capacity; + } + + return item; + } + + isEmpty() { return this.#size === 0; } + isFull() { return this.#size === this.#capacity; } + get size() { return this.#size; } +} + +// Hot Potato Game Function +function hotPotato(players, numPasses) { + const queue = new CircularQueue(players.length); + for (const player of players) { + queue.enqueue(player); + } + + while (queue.size > 1) { + for (let i = 0; i < numPasses; i++) { + queue.enqueue(queue.dequeue()); + } + console.log(`${queue.dequeue()} is eliminated!`); + } + + return queue.dequeue(); // The winner +} + +// Example Usage +const players = ["Violet", "Feyre", "Poppy", "Oraya", "Aelin"]; +const winner = hotPotato(players, 7); +console.log(`The winner is: ${winner}!`); + +// Output +// Poppy is eliminated! +// Feyre is eliminated! +// Aelin is eliminated! +// Oraya is eliminated! +// The winner is: Violet! + +// to see the output of this file use the command: node src/05-queue-deque/hot-potato.js \ No newline at end of file diff --git a/src/05-queue-deque/leetcode/ex.md b/src/05-queue-deque/leetcode/ex.md new file mode 100644 index 00000000..f6a1b8cb --- /dev/null +++ b/src/05-queue-deque/leetcode/ex.md @@ -0,0 +1,36 @@ +Explanation + +Count Preferences: + +Create an array counts to store how many students want each sandwich type. +Iterate through the students array. For each student, increment the corresponding count in the counts array. +Process Sandwiches: + +Iterate through the sandwiches array. For each sandwich: +If the count for that sandwich type is zero, it means no one left wants it. Break the loop. +Otherwise, decrement the count for that sandwich type, as one student took it. +Return Result: + +After processing all sandwiches (or breaking early), the remaining counts in the counts array represent the students who didn't get their preferred sandwich. Add these counts and return the result. +Why This is More Efficient + +Avoiding Array Manipulation: The optimized version avoids repeated shifting and pushing of elements in the students array. This reduces the computational overhead, especially with large inputs. + +Early Termination: By checking if the count of a sandwich type reaches zero, the loop can exit early, potentially saving many iterations if a lot of sandwiches remain that no one wants. + +Simpler Logic: The logic is more straightforward, making it easier to understand and maintain. + +Example + +Let's use the same example as before: + +students = [1, 1, 0, 0] +sandwiches = [0, 1, 0, 1] +counts becomes [2, 2] (two students for each type). +The first sandwich (0) is taken, counts is now [1, 2]. +The second sandwich (1) is taken, counts is now [1, 1]. +This continues until all sandwiches are gone and counts becomes [0, 0]. +The function returns 0 + 0 = 0. +Key Improvement + +The main optimization comes from avoiding the costly includes check and array manipulations inside the loop. Instead, we directly track the number of students who still want each sandwich type. This allows us to efficiently determine when no one wants a particular sandwich anymore, leading to early termination. \ No newline at end of file diff --git a/src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts b/src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts new file mode 100644 index 00000000..a09c1109 --- /dev/null +++ b/src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts @@ -0,0 +1,74 @@ +// Path: src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts +// https://leetcode.com/problems/number-of-students-unable-to-eat-lunch/ + +// 1700. Number of Students Unable to Eat Lunch +// Easy + +// hint 1: Simulate the process of giving sandwiches to students. +// hint 2: Calculate those who will eat instead of those who will not. + +// Use two queues to simulate the process of giving sandwiches to students. + +function countStudents(students: number[], sandwiches: number[]): number { + + let count = 0; + while(students.length > 0 && count < students.length) { + if(students[0] === sandwiches[0]) { + students.shift(); + sandwiches.shift(); + count = 0; + } else { + students.push(students.shift()); + count++; + } + } + + return students.length; +}; + +// Time complexity: O(n) +// Space complexity: O(n) + +// Input: students = [1,1,0,0], sandwiches = [0,1,0,1] +// Output: 0 +console.log(countStudents([1,1,0,0], [0,1,0,1])); // 0 +console.log(countStudents([1,1,1,0,0,1], [1,0,0,0,1,1])); // 3 + +// optimized solution +function countStudents2(students: number[], sandwiches: number[]): number { + while (students.length > 0) { + if (students[0] === sandwiches[0]) { + students.shift(); + sandwiches.shift(); + } else { + // check if there is a student who prefers the sandwich on the top of the stack to continue the loop + if (students.includes(sandwiches[0])) { + let num = students.shift(); + students.push(num); + } else { + break; // if there is no student who prefers the sandwich on the top of the stack, break the loop + } + } + } + return students.length; +}; + +// Time complexity: O(nΛ†2) +// Space complexity: O(1) + +function countStudentsOptimized(students, sandwiches) { + const counts = [0, 0]; // Track counts of each preference (0 or 1) + for (const student of students) { + counts[student]++; + } + + for (const sandwich of sandwiches) { + if (counts[sandwich] === 0) { + break; // No one wants this sandwich type anymore + } + counts[sandwich]--; + } + + return counts[0] + counts[1]; // Remaining students who couldn't eat + } + \ No newline at end of file diff --git a/src/05-queue-deque/leetcode/time-needed-to-buy-tickets.ts b/src/05-queue-deque/leetcode/time-needed-to-buy-tickets.ts new file mode 100644 index 00000000..91a7ae3d --- /dev/null +++ b/src/05-queue-deque/leetcode/time-needed-to-buy-tickets.ts @@ -0,0 +1,30 @@ +// Path: src/05-queue-deque/leetcode/time-needed-to-buy-tickets.ts +// https://leetcode.com/problems/time-needed-to-buy-tickets/ + +// 2034. Time Needed to Buy Tickets +// Medium + +function timeRequiredToBuy(tickets: number[], k: number): number { + + let queue = tickets.map((t, i) => [t, i]); + let time = 0; + while(queue.length > 0) { + let [t, i] = queue.shift(); + if(t - k <= 0) { + time += t; + break; + } else { + time += k; + queue.push([t - k, i]); + } + } + + return time; +}; + +// Time complexity: O(n) +// Space complexity: O(n) + +console.log(timeRequiredToBuy([2,6,3,4,5], 2)); // 14 +console.log(timeRequiredToBuy([2,3,2], 2)); // 6 +console.log(timeRequiredToBuy([5,1,1,1], 0)); // 11 diff --git a/src/05-queue-deque/palindrome-checker.js b/src/05-queue-deque/palindrome-checker.js new file mode 100644 index 00000000..25ebb3da --- /dev/null +++ b/src/05-queue-deque/palindrome-checker.js @@ -0,0 +1,27 @@ +const Deque = require('./deque'); + +function isPalindrome(word) { + + if (word === undefined || word === null || (typeof word === 'string' && word.length === 0)) { + return false; + } + + const deque = new Deque(); + word = word.toLowerCase().replace(/\s/g, ''); + + for (let i = 0; i < word.length; i++) { + deque.addRear(word[i]); + } + + // Check if the word is a palindrome + while (deque.size() > 1) { + if (deque.removeFront() !== deque.removeRear()) { + return false; + } + } + + return true; +} + +// Test the palindrome checker +console.log(isPalindrome("racecar")); // Output: true \ No newline at end of file diff --git a/src/05-queue-deque/queue.js b/src/05-queue-deque/queue.js new file mode 100644 index 00000000..abbf3bd7 --- /dev/null +++ b/src/05-queue-deque/queue.js @@ -0,0 +1,46 @@ +// src/05-queue-deque/queue.js + +class Queue { + + #items = []; + + enqueue(item) { + this.#items.push(item); + } + + dequeue() { + return this.#items.shift(); + } + + front() { + return this.#items[0]; + } + + isEmpty() { + return this.#items.length === 0; + } + + get size() { + return this.#items.length; + } + + clear() { + this.#items = []; + } + + toString() { + if (this.isEmpty()) { + return 'Empty Queue'; + } else { + return this.#items.map(item => { // {1} + if (typeof item === 'object' && item !== null) { // {2} + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types {3} + } + }).join(', '); // {4} + } + } +} + +module.exports = Queue; \ No newline at end of file diff --git a/src/05-queue-deque/queue.ts b/src/05-queue-deque/queue.ts new file mode 100644 index 00000000..4d795e7b --- /dev/null +++ b/src/05-queue-deque/queue.ts @@ -0,0 +1,52 @@ +// src/05-queue-deque/queue.ts + +class Queue { + + private items: T[] = []; + + enqueue(item: T): void { + this.items.push(item); + } + + dequeue(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items.shift(); + } + + front(): T | undefined { + if (this.isEmpty()) { + return undefined; + } + return this.items[0]; + } + + isEmpty(): boolean { + return this.items.length === 0; + } + + get size(): number { + return this.items.length; + } + + clear(): void { + this.items = []; + } + + toString(): string { + if (this.isEmpty()) { + return 'Empty Queue'; + } else { + return this.items.map(item => { // {1} + if (typeof item === 'object' && item !== null) { // {2} + return JSON.stringify(item); // Handle objects + } else { + return item.toString(); // Handle other types {3} + } + }).join(', '); // {4} + } + } +} + +export default Queue; \ No newline at end of file From 64dbd84695bbd835da8692c3e09c0fce177ea1fb Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 14 Jun 2024 11:42:46 -0400 Subject: [PATCH 27/43] fixed chapter 5 tests --- src/05-queue-deque/__test__/deque.test.ts | 2 +- src/05-queue-deque/__test__/queue.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/05-queue-deque/__test__/deque.test.ts b/src/05-queue-deque/__test__/deque.test.ts index 5fe61ceb..4a79feae 100644 --- a/src/05-queue-deque/__test__/deque.test.ts +++ b/src/05-queue-deque/__test__/deque.test.ts @@ -69,7 +69,7 @@ describe('Deque', () => { test('should return the size of the deque', () => { deque.addFront(1); deque.addFront(2); - expect(deque.size()).toBe(2); + expect(deque.size).toBe(2); }); test('should clear the deque', () => { diff --git a/src/05-queue-deque/__test__/queue.test.ts b/src/05-queue-deque/__test__/queue.test.ts index 020666b7..e38b597b 100644 --- a/src/05-queue-deque/__test__/queue.test.ts +++ b/src/05-queue-deque/__test__/queue.test.ts @@ -31,7 +31,7 @@ describe('Queue', () => { queue.dequeue(); queue.dequeue(); - expect(queue.size()).toBe(1); + expect(queue.size).toBe(1); }); test('should return the correct front element after enqueueing and dequeueing elements', () => { @@ -56,7 +56,7 @@ describe('Queue', () => { queue.clear(); expect(queue.isEmpty()).toBe(true); - expect(queue.size()).toBe(0); + expect(queue.size).toBe(0); }); test('should convert the queue to a string', () => { From 2633b83e33960f90a7d2f6b59f627bf4037fd980 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 4 Jul 2024 15:55:25 -0400 Subject: [PATCH 28/43] chore: Update tsconfig.json to include "lib" option for ESNext --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 4771fdd1..3cafd3f4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ESNext", "module": "CommonJS", + "lib": ["ESNext"], //"outDir": "dist", "rootDir": "src", // "strict": true, From 174bed9a0359bcf4c02c4351f89a943ae7becdf0 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 4 Jul 2024 15:56:18 -0400 Subject: [PATCH 29/43] chapter 6: linked lists --- .../__test__/doubly-linked-list.test.ts | 122 ++++++++++ .../__test__/linked-list.test.ts | 165 +++++++++++++ src/06-linked-list/circular-linked-list.js | 193 +++++++++++++++ src/06-linked-list/circular-linked-list.ts | 161 +++++++++++++ src/06-linked-list/doubly-linked-list.js | 220 ++++++++++++++++++ src/06-linked-list/doubly-linked-list.ts | 188 +++++++++++++++ .../leetcode/reverse-linked-list.ts | 34 +++ src/06-linked-list/linked-list.ts | 146 ++++++++++++ src/06-linked-list/linked-list_.js | 164 +++++++++++++ src/06-linked-list/media-player.js | 141 +++++++++++ 10 files changed, 1534 insertions(+) create mode 100644 src/06-linked-list/__test__/doubly-linked-list.test.ts create mode 100644 src/06-linked-list/__test__/linked-list.test.ts create mode 100644 src/06-linked-list/circular-linked-list.js create mode 100644 src/06-linked-list/circular-linked-list.ts create mode 100644 src/06-linked-list/doubly-linked-list.js create mode 100644 src/06-linked-list/doubly-linked-list.ts create mode 100644 src/06-linked-list/leetcode/reverse-linked-list.ts create mode 100644 src/06-linked-list/linked-list.ts create mode 100644 src/06-linked-list/linked-list_.js create mode 100644 src/06-linked-list/media-player.js diff --git a/src/06-linked-list/__test__/doubly-linked-list.test.ts b/src/06-linked-list/__test__/doubly-linked-list.test.ts new file mode 100644 index 00000000..2473c527 --- /dev/null +++ b/src/06-linked-list/__test__/doubly-linked-list.test.ts @@ -0,0 +1,122 @@ +import {describe, expect, test, beforeEach} from '@jest/globals'; +import DoublyLinkedList from '../doubly-linked-list'; + +describe('DoublyLinkedList', () => { + let doublyLinkedList: DoublyLinkedList; + + beforeEach(() => { + doublyLinkedList = new DoublyLinkedList(); + }); + + test('should create an empty doubly linked list', () => { + expect(doublyLinkedList.toString()).toBe(''); + }); + + test('should append node to doubly linked list', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + doublyLinkedList.append(3); + expect(doublyLinkedList.toString()).toBe('1, 2, 3'); + }); + + test('should prepend node to doubly linked list', () => { + doublyLinkedList.prepend(2); + expect(doublyLinkedList.toString()).toBe('2'); + doublyLinkedList.append(1); + doublyLinkedList.prepend(3); + expect(doublyLinkedList.toString()).toBe('3, 2, 1'); + }); + + test('should insert node at position 0', () => { + doublyLinkedList.append(1); + doublyLinkedList.insert(0, 2); + expect(doublyLinkedList.toString()).toBe('2, 1'); + }); + + test('should insert node at given position', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(3); + doublyLinkedList.insert(1, 2); + expect(doublyLinkedList.toString()).toBe('1, 2, 3'); + }); + + test('should insert node at invalid position', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(3); + expect(doublyLinkedList.insert(3, 2)).toBe(false); + expect(doublyLinkedList.toString()).toBe('1, 3'); + }); + + test('should remove node from doubly linked list', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + doublyLinkedList.append(3); + doublyLinkedList.removeAt(1); + expect(doublyLinkedList.toString()).toBe('1, 3'); + }); + + test('should remove node at position 0', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + doublyLinkedList.removeAt(0); + expect(doublyLinkedList.toString()).toBe('2'); + }); + + test('should remove node at invalid position', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(3); + expect(doublyLinkedList.removeAt(3)).toBe(false); + expect(doublyLinkedList.toString()).toBe('1, 3'); + }); + + test('should remove element from doubly linked list', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + doublyLinkedList.append(3); + doublyLinkedList.remove(3); + expect(doublyLinkedList.toString()).toBe('1, 2'); + doublyLinkedList.remove(1); + expect(doublyLinkedList.toString()).toBe('2'); + doublyLinkedList.remove(2); + expect(doublyLinkedList.toString()).toBe(''); + }); + + test('should remove element that is not in doubly linked list', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + expect(doublyLinkedList.remove(3)).toBe(false); + expect(doublyLinkedList.toString()).toBe('1, 2'); + }); + + test('should find element in doubly linked list', () => { + doublyLinkedList.append(1); + doublyLinkedList.append(2); + doublyLinkedList.append(3); + expect(doublyLinkedList.indexOf(1)).toBe(0); + expect(doublyLinkedList.indexOf(2)).toBe(1); + expect(doublyLinkedList.indexOf(3)).toBe(2); + expect(doublyLinkedList.indexOf(4)).toBe(-1); + }); + + test('should check if doubly linked list is empty', () => { + expect(doublyLinkedList.isEmpty()).toBe(true); + doublyLinkedList.append(1); + expect(doublyLinkedList.isEmpty()).toBe(false); + }); + + test('should return the size of the doubly linked list', () => { + expect(doublyLinkedList.getSize()).toBe(0); + doublyLinkedList.append(1); + expect(doublyLinkedList.getSize()).toBe(1); + doublyLinkedList.append(2); + expect(doublyLinkedList.getSize()).toBe(2); + }); + + test('should convert doubly linked list to string', () => { + doublyLinkedList.append(1); + expect(doublyLinkedList.toString()).toBe('1'); + doublyLinkedList.append(2); + expect(doublyLinkedList.toString()).toBe('1, 2'); + }); + +}); \ No newline at end of file diff --git a/src/06-linked-list/__test__/linked-list.test.ts b/src/06-linked-list/__test__/linked-list.test.ts new file mode 100644 index 00000000..ab9d879a --- /dev/null +++ b/src/06-linked-list/__test__/linked-list.test.ts @@ -0,0 +1,165 @@ +import {describe, expect, test, beforeEach} from '@jest/globals'; +import LinkedList from '../linked-list'; + +describe('LinkedList', () => { + let linkedList: LinkedList; + + beforeEach(() => { + linkedList = new LinkedList(); + }); + + test('should create an empty linked list', () => { + expect(linkedList.toString()).toBe(''); + }); + + test('should append node to linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + expect(linkedList.toString()).toBe('1, 2, 3'); + }); + + test('should prepend node to linked list', () => { + linkedList.prepend(2); + expect(linkedList.toString()).toBe('2'); + linkedList.append(1); + linkedList.prepend(3); + expect(linkedList.toString()).toBe('3, 2, 1'); + }); + + test('should insert node at position 0', () => { + linkedList.append(1); + linkedList.insert(0, 2); + expect(linkedList.toString()).toBe('2, 1'); + }); + + test('should insert node at given position', () => { + linkedList.append(1); + linkedList.append(3); + linkedList.insert(1, 2); + expect(linkedList.toString()).toBe('1, 2, 3'); + }); + + test('should insert node at invalid position', () => { + linkedList.append(1); + linkedList.append(3); + expect(linkedList.insert(3, 2)).toBe(false); + expect(linkedList.toString()).toBe('1, 3'); + }); + + test('should remove node from linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + linkedList.removeAt(1); + expect(linkedList.toString()).toBe('1, 3'); + }); + + test('should remove node at position 0', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.removeAt(0); + expect(linkedList.toString()).toBe('2'); + }); + + test('should remove node at invalid position', () => { + linkedList.append(1); + linkedList.append(2); + expect(() => linkedList.removeAt(2)).toThrowError('Invalid position'); + }); + + test('should remove element from linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + linkedList.remove(2); + expect(linkedList.toString()).toBe('1, 3'); + }); + + test('should remove element from linked list head', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.remove(1); + expect(linkedList.toString()).toBe('2'); + }); + + test('should remove element from linked list tail', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.remove(2); + expect(linkedList.toString()).toBe('1'); + }); + + test('should return null if element was not found', () => { + linkedList.append(1); + linkedList.append(2); + expect(linkedList.remove(3)).toBeNull(); + }); + + test('should find element index in linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + expect(linkedList.indexOf(1)).toBe(0); + expect(linkedList.indexOf(2)).toBe(1); + expect(linkedList.indexOf(3)).toBe(2); + }); + + test('should return -1 when element was not found', () => { + linkedList.append(1); + linkedList.append(2); + expect(linkedList.indexOf(3)).toBe(-1); + }); + + test('should return true if list is empty', () => { + expect(linkedList.isEmpty()).toBe(true); + }); + + test('should return false if list is not empty', () => { + linkedList.append(1); + expect(linkedList.isEmpty()).toBe(false); + }); + + test('should return the size of the linked list', () => { + expect(linkedList.getSize()).toBe(0); + linkedList.append(1); + expect(linkedList.getSize()).toBe(1); + linkedList.append(2); + expect(linkedList.getSize()).toBe(2); + }); + + test('should convert linked list to string', () => { + linkedList.append(1); + expect(linkedList.toString()).toBe('1'); + linkedList.append(2); + expect(linkedList.toString()).toBe('1, 2'); + }); + + test('should convert linked list to string with custom stringifier', () => { + const customList = new LinkedList<{value: number; key: string}>(); + customList.append({value: 1, key: 'key1'}); + expect(customList.toString()).toBe('{"value":1,"key":"key1"}'); + }); + + test('should reverse the linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + linkedList.reverse(); + expect(linkedList.toString()).toBe('3, 2, 1'); + }); + + test('should reverse the linked list with one element', () => { + linkedList.append(1); + linkedList.reverse(); + expect(linkedList.toString()).toBe('1'); + }); + + test('should clear the linked list', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + linkedList.clear(); + expect(linkedList.toString()).toBe(''); + }); +}); \ No newline at end of file diff --git a/src/06-linked-list/circular-linked-list.js b/src/06-linked-list/circular-linked-list.js new file mode 100644 index 00000000..62ea7f46 --- /dev/null +++ b/src/06-linked-list/circular-linked-list.js @@ -0,0 +1,193 @@ +// src/06-linked-list/circular-linked-list.js + +class LinkedListNode { + constructor(data, next = null) { + this.data = data; + this.next = next; + } +} + +class CircularLinkedList { + + #head; + #size = 0; + + append(data) { + const newNode = new LinkedListNode(data); + if (!this.#head) { // empty list + this.#head = newNode; + newNode.next = this.#head; // point to itself + } else { // non-empty list + let current = this.#head; + while (current.next !== this.#head) { + current = current.next; + } + current.next = newNode; + newNode.next = this.#head; // circular reference + } + this.#size++; + } + + prepend(data) { + const newNode = new LinkedListNode(data, this.#head); + if (!this.head) { + this.head = newNode; + newNode.next = this.head; // make it circular + } else { + // Find the last node + let current = this.head; + while (current.next !== this.head) { + current = current.next; + } + current.next = newNode; + this.head = newNode; + } + this.#size++; + } + + insert(data, position) { + if (this.#isInvalidPosition(position)) { + return false; + } + const node = new LinkedListNode(data, null); + if (position === 0) { + this.prepend(data); + return true; + } + let current = this.#head; + let previous = null; + let index = 0; + while (index++ < position) { + previous = current; + current = current.next; + } + node.next = current; + previous.next = node; + this.#size++; + return true; + } + + removeAt(position) { + if (this.#size === 0) { + throw new RangeError('Cannot remove from an empty list.'); + } + if (this.#isInvalidPosition(position)) { + throw new RangeError('Invalid position.'); + } + if (position === 0) { + return this.#removeFromHead(); + } else if (position === this.#size - 1) { + return this.#removeFromTail(); + } else { + return this.#removeFromMiddle(position); + } + } + + #removeFromHead() { + const nodeToRemove = this.#head; + let lastNode = this.#head; + while (lastNode.next !== this.#head) { // Find the last node + lastNode = lastNode.next; + } + this.#head = nodeToRemove.next; // skip the head + lastNode.next = this.#head; // make it circular + + if (this.#size === 1) { // only one node + this.#head = null; + } + this.#size--; + return nodeToRemove.data; + } + + #removeFromTail() { + if (this.#head.next === this.#head) { // single node case + const nodeToRemove = this.#head; + this.#head = null; + this.#size--; + return nodeToRemove.data; + } else { + let lastNode = this.#head; + let previousNode = null; + while (lastNode.next !== this.#head) { // Find the last node + previousNode = lastNode; + lastNode = lastNode.next; + } + previousNode.next = this.#head; // skip the last node to remove it + this.#size--; + return lastNode.data; + } + } + + + #isInvalidPosition(position) { + return position < 0 || position >= this.#size; + } + + get size() { + return this.#size; + } + + isEmpty() { + return this.#size === 0; + } + + clear() { + this.#head = null; + this.#size = 0; + } + + remove(data: T): T | null { + const index = this.indexOf(data); + if (index === -1) { + return null; + } + return this.removeAt(index); + } + + indexOf(data: T): number { + let current = this.#head; + let index = 0; + while (current) { + if (current.data === data) { + return index; + } + index++; + current = current.next; + if (current === this.#head) { + return -1; + } + } + return -1; + } + + toString() { + let current = this.#head; + let result = ''; + while (current) { + result += current.data + (current.next !== this.#head ? ' -> ' : ''); + current = current.next; + if (current === this.#head) { + break; + } + } + return result; + } + + reverse() { + let current = this.#head; + let previous = null; + let next = null; + while (current) { + next = current.next; + current.next = previous; + previous = current; + current = next; + if (current === this.#head) { + break; + } + } + this.#head = previous; + } +} + +export default CircularLinkedList; diff --git a/src/06-linked-list/circular-linked-list.ts b/src/06-linked-list/circular-linked-list.ts new file mode 100644 index 00000000..5152ad37 --- /dev/null +++ b/src/06-linked-list/circular-linked-list.ts @@ -0,0 +1,161 @@ +// src/06-linked-list/circular-linked-list.ts + +class LinkedListNode { + constructor(public element: T, public next?: LinkedListNode) {} +} + +class CircularLinkedList { + + private head: LinkedListNode | null; + private size = 0; + + append(element: T) { + const node = new LinkedListNode(element, null); + if (!this.head) { + this.head = node; + node.next = this.head; + } else { + let current = this.head; + while (current.next !== this.head) { + current = current.next; + } + current.next = node; + node.next = this.head; + } + this.size++; + } + + prepend(element: T) { + const node = new LinkedListNode(element, this.head); + let current = this.head; + while (current.next !== this.head) { + current = current.next; + } + current.next = node; + this.head = node; + this.size++; + } + + insert(position: number, element: T): boolean { + if (this.isInvalidPosition(position)) { + return false; + } + const node = new LinkedListNode(element, null); + if (position === 0) { + this.prepend(element); + return true; + } + let current = this.head; + let previous = null; + let index = 0; + while (index++ < position) { + previous = current; + current = current.next; + } + node.next = current; + previous.next = node; + this.size++; + return true; + } + + removeAt(position: number): T { + if (this.isInvalidPosition(position)) { + throw new Error('Invalid position'); + } + let current = this.head; + let previous = null; + if (position === 0) { + let last = this.head; + while (last.next !== this.head) { + last = last.next; + } + this.head = current.next; + last.next = this.head; + } else { + let index = 0; + while (index++ < position) { + previous = current; + current = current.next; + } + previous.next = current.next; + } + this.size--; + return current.element; + } + + private isInvalidPosition(position: number): boolean { + return position < 0 || position >= this.size; + } + + getSize(): number { + return this.size; + } + + isEmpty(): boolean { + return this.size === 0; + } + + getHead(): LinkedListNode | null { + return this.head; + } + + clear() { + this.head = null; + this.size = 0; + } + + remove(element: T): T | null { + const index = this.indexOf(element); + if (index === -1) { + return null; + } + return this.removeAt(index); + } + + indexOf(element: T): number { + let current = this.head; + let index = 0; + while (current) { + if (current.element === element) { + return index; + } + index++; + current = current.next; + if (current === this.head) { + return -1; + } + } + return -1; + } + + toString() { + let current = this.head; + let result = ''; + while (current) { + result += current.element + (current.next !== this.head ? ' -> ' : ''); + current = current.next; + if (current === this.head) { + break; + } + } + return result; + } + + reverse() { + let current = this.head; + let previous = null; + let next = null; + while (current) { + next = current.next; + current.next = previous; + previous = current; + current = next; + if (current === this.head) { + break; + } + } + this.head = previous; + } +} + +export default CircularLinkedList; diff --git a/src/06-linked-list/doubly-linked-list.js b/src/06-linked-list/doubly-linked-list.js new file mode 100644 index 00000000..32a4fef7 --- /dev/null +++ b/src/06-linked-list/doubly-linked-list.js @@ -0,0 +1,220 @@ +// src/06-linked-list/doubly-linked-list.js + +class DoublyLinkedListNode { + constructor(data, next = null, previous = null) { + this.data = data; + this.next = next; + this.previous = previous; // new + } +} + +class DoublyLinkedList { + #head; + #tail; + #size = 0; + + append(data) { + const newNode = new DoublyLinkedListNode(data); + if (!this.#head) { // empty list + this.#head = newNode; + this.#tail = newNode; + } else { // non-empty list + newNode.previous = this.#tail; + this.#tail.next = newNode; + this.#tail = newNode; + } + this.#size++; + } + + prepend(data) { + const newNode = new DoublyLinkedListNode(data); + if (!this.#head) { // empty list + this.#head = newNode; + this.#tail = newNode; + } else { // non-empty list + newNode.next = this.#head; + this.#head.previous = newNode; + this.#head = newNode; + } + this.#size++; + } + + insert(data, position) { + if (this.#isInvalidPosition(position)) { + return false; + } + + if (position === 0) { // first position + this.prepend(data); + return true; + } + + if (position === this.#size) { // last position + this.append(data); + return true; + } + + // middle position + return this.#insertInTheMiddle(data, position); + } + + #insertInTheMiddle(data, position) { + const newNode = new DoublyLinkedListNode(data); + let currentNode = this.#head; + let previousNode; + + for (let index = 0; index < position; index++) { + previousNode = currentNode; + currentNode = currentNode.next; + } + + newNode.next = currentNode; + newNode.previous = previousNode; + currentNode.previous = newNode; + previousNode.next = newNode; + + this.#size++; + return true; + } + + removeAt(position) { + if (this.#size === 0) { + throw new RangeError('Cannot remove from an empty list.'); + } + if (this.#isInvalidPosition(position)) { + throw new RangeError('Invalid position.'); + } + + if (position === 0) { + return this.#removeFromHead(); + } + if (position === this.#size - 1) { + return this.#removeFromTail(); + } + return this.#removeFromMiddle(position); + } + + #removeFromHead() { + const nodeToRemove = this.#head; + this.#head = nodeToRemove.next; + if (this.#head) { + this.#head.previous = null; + } else { + this.#tail = null; // List becomes empty + } + this.#size--; + nodeToRemove.next = null; + return nodeToRemove.data; + } + + #removeFromTail() { + const nodeToRemove = this.#tail; + this.#tail = nodeToRemove.previous; + if (this.#tail) { + this.#tail.next = null; + } else { + this.#head = null; // List becomes empty + } + this.#size--; + nodeToRemove.previous = null; + return nodeToRemove.data; + } + + #removeFromMiddle(position) { + let nodeToRemove = this.#head; + let previousNode; + for (let index = 0; index < position; index++) { + previousNode = nodeToRemove; + nodeToRemove = nodeToRemove.next; + } + + previousNode.next = nodeToRemove.next; + nodeToRemove.next.previous = previousNode; + + nodeToRemove.next = null; + nodeToRemove.previous = null; + + this.#size--; + return nodeToRemove.data; + } + + #isInvalidPosition(position) { + return position < 0 || position >= this.#size; + } + + get size() { + return this.#size; + } + + isEmpty() { + return this.#size === 0; + } + + indexOf(data, compareFunction = (a, b) => a === b) { + let current = this.#head; + let index = 0; + while (current) { + if (compareFunction(current.data, data)) { + return index; + } + index++; + current = current.next; + } + return -1; + } + + + clear() { + this.#head = null; + this.#size = 0; + } + + toString() { + let current = this.#head; + let objString = ''; + while (current) { + objString += this.elementToString(current.data); + current = current.next; + if (current) { + objString += ', '; + } + } + return objString; + } + + // inverse toString + inverseToString() { + let current = this.#tail; + let objString = ''; + while (current) { + objString += this.elementToString(current.data); + current = current.previous; + } + return objString; + } + + private elementToString(data: T): string { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + reverse() { + let current = this.#head; + let previous = null; + let next = null; + while (current) { + next = current.next; + current.next = previous; + current.previous = next; + previous = current; + current = next; + } + this.#tail = this.#head; + this.#head = previous; + } +} + +export default DoublyLinkedList; \ No newline at end of file diff --git a/src/06-linked-list/doubly-linked-list.ts b/src/06-linked-list/doubly-linked-list.ts new file mode 100644 index 00000000..b77005b6 --- /dev/null +++ b/src/06-linked-list/doubly-linked-list.ts @@ -0,0 +1,188 @@ +// src/06-linked-list/doubly-linked-list.ts + +class DoublyLinkedListNode { + constructor( + public data: T, + public next?: DoublyLinkedListNode, + public previous?: DoublyLinkedListNode + ) {} +} + +class DoublyLinkedList { + private head: DoublyLinkedListNode | null; + private tail: DoublyLinkedListNode | null; + private size = 0; + + append(data: T) { + const node = new DoublyLinkedListNode(data); + if (!this.head) { + this.head = node; + this.tail = node; + } else { + node.previous = this.tail; + this.tail.next = node; + this.tail = node; + } + this.size++; + } + + prepend(data: T) { + const node = new DoublyLinkedListNode(data); + if (!this.head) { + this.head = node; + this.tail = node; + } else { + node.next = this.head; + this.head.previous = node; + this.head = node; + } + this.size++; + } + + insert(data: T, position: number): boolean { + if (this.isInvalidPosition(position)) { + return false; + } + if (position === 0) { + this.prepend(data); + return true; + } + if (position === this.size) { + this.append(data); + return true; + } + const newNode = new DoublyLinkedListNode(data); + let current = this.head; + let previous = null; + for (let index = 0; index < position; index++) { + previous = current; + current = current.next; + } + newNode.next = current; + newNode.previous = previous; + current.previous = newNode; + previous.next = newNode; + this.size++; + return true; + } + + removeAt(position: number): T { + if (this.isInvalidPosition(position)) { + throw new RangeError('Invalid position'); + } + let nodeToRemove = this.head; + let previousNode = null; + + if (position === 0) { + this.head = nodeToRemove.next; + if (this.head) { // Check if new head exists + this.head.previous = null; + } else { + this.tail = null; // List becomes empty + } + } else if (position === this.size - 1) { + nodeToRemove = this.tail; + this.tail = nodeToRemove.previous; + this.tail.next = null; + } else { + let index = 0; + while (index < position) { + previousNode = nodeToRemove; + nodeToRemove = nodeToRemove.next; + index++; + } + previousNode.next = nodeToRemove.next; + nodeToRemove.next.previous = previousNode; + } + this.size--; + return nodeToRemove.data; + } + + private isInvalidPosition(position: number) { + return position < 0 || position >= this.size; + } + + getSize() { + return this.size; + } + + isEmpty() { + return this.size === 0; + } + + indexOf(data: T) { + let current = this.head; + let index = 0; + while (current) { + if (current.data === data) { + return index; + } + index++; + current = current.next; + } + return -1; + } + + remove(data: T): T | null { + const index = this.indexOf(data); + if (index === -1) { + return null; + } + return this.removeAt(index); + } + + clear() { + this.head = null; + this.tail = null; + this.size = 0; + } + + toString() { + let current = this.head; + let objString = ''; + while (current) { + objString += this.elementToString(current.data); + current = current.next; + if (current) { + objString += ', '; + } + } + return objString; + } + + // inverse toString + inverseToString() { + let current = this.tail; + let objString = ''; + while (current) { + objString += this.elementToString(current.data); + current = current.previous; + } + return objString; + } + + private elementToString(data: T): string { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + reverse() { + let current = this.head; + let previous = null; + let next = null; + while (current) { + next = current.next; + current.next = previous; + current.previous = next; + previous = current; + current = next; + } + this.tail = this.head; + this.head = previous; + } +} + +export default DoublyLinkedList; \ No newline at end of file diff --git a/src/06-linked-list/leetcode/reverse-linked-list.ts b/src/06-linked-list/leetcode/reverse-linked-list.ts new file mode 100644 index 00000000..d910426d --- /dev/null +++ b/src/06-linked-list/leetcode/reverse-linked-list.ts @@ -0,0 +1,34 @@ + +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +class ListNode { + val: number + next: ListNode | null + constructor(val?: number, next?: ListNode | null) { + this.val = (val === undefined ? 0 : val) + this.next = (next === undefined ? null : next) + } +} + +function reverseList(head: ListNode | null): ListNode | null { + let current = head; + let newHead = null; + let nextNode = null; + while (current) { + nextNode = current.next; + current.next = newHead; + newHead = current; + current = nextNode; + } + return newHead; +} \ No newline at end of file diff --git a/src/06-linked-list/linked-list.ts b/src/06-linked-list/linked-list.ts new file mode 100644 index 00000000..cf59437b --- /dev/null +++ b/src/06-linked-list/linked-list.ts @@ -0,0 +1,146 @@ +// src/06-linked-list/linked-list.ts + +class LinkedListNode { + constructor(public element: T, public next?: LinkedListNode) {} +} + +class LinkedList { + + private head: LinkedListNode | null; + private size = 0; + + append(element: T) { + const node = new LinkedListNode(element, null); + if (!this.head) { + this.head = node; + } else { + let current = this.head; + while (current.next) { + current = current.next; + } + current.next = node; + } + this.size++; + } + + prepend(element: T) { + const node = new LinkedListNode(element, this.head); + this.head = node; + this.size++; + } + + insert(position: number, element: T): boolean { + if (this.isInvalidPosition(position)) { + return false; + } + const node = new LinkedListNode(element, null); + if (position === 0) { + this.prepend(element); + return true + } + let current = this.head; + let previous = null; + let index = 0; + while (index++ < position) { + previous = current; + current = current.next; + } + node.next = current; + previous.next = node; + this.size++; + return true; + } + + removeAt(position: number): T { + if (this.isInvalidPosition(position)) { + throw new Error('Invalid position'); + } + let current = this.head; + let previous = null; + if (position === 0) { + this.head = current.next; + } else { + for (let index = 0; index < position; index++) { + previous = current; + current = current.next; + } + previous.next = current.next; + } + this.size--; + return current.element; + } + + private isInvalidPosition(position: number) { + return position < 0 || position >= this.size; + } + + remove(element: T): T | null { + const index = this.indexOf(element); + if (index === -1) { + return null; + } + return this.removeAt(index); + } + + indexOf(element: T) { + let current = this.head; + let index = 0; + while (current) { + if (current.element === element) { + return index; + } + index++; + current = current.next; + } + return -1; + } + + isEmpty() { + return this.size === 0; + } + + clear() { + this.head = null; + this.size = 0; + } + + getSize() { + return this.size; + } + + reverse() { + let current = this.head; + let previous = null; + let next = null; + while (current) { + next = current.next; + current.next = previous; + previous = current; + current = next; + } + this.head = previous; + } + + toString() { + let current = this.head; + let objString = ''; + while (current) { + objString += this.elementToString(current.element); + current = current.next; + if (current) { + objString += ', '; + } + } + return objString; + } + + private elementToString(element: T): string { + if (typeof element === 'object' && element !== null) { + return JSON.stringify(element); + } else { + return element.toString(); + } + } +} + +export default LinkedList; \ No newline at end of file diff --git a/src/06-linked-list/linked-list_.js b/src/06-linked-list/linked-list_.js new file mode 100644 index 00000000..18ef5586 --- /dev/null +++ b/src/06-linked-list/linked-list_.js @@ -0,0 +1,164 @@ +// src/06-linked-list/linked-list.js + +class LinkedListNode { + constructor(data, next = null) { + this.data = data; + this.next = next; + } +} + +class LinkedList { + + #head; + #size = 0; + + append(data) { + const newNode = new LinkedListNode(data); + if (!this.#head) { + this.#head = newNode; + } else { + let current = this.#head; + while (current.next !== null) { + current = current.next; + } + current.next = newNode; + } + this.#size++; + } + + prepend(data) { + const newNode = new LinkedListNode(data, this.#head); + this.#head = newNode; + this.#size++; + } + + insert(data, position) { + if (this.#isInvalidPosition(position)) { + return false; + } + const newNode = new LinkedListNode(data); + if (position === 0) { + this.prepend(data); + return true; + } + let current = this.#head; + let previous = null; + let index = 0; + while (index++ < position) { + previous = current; + current = current.next; + } + newNode.next = current; + previous.next = newNode; + this.#size++; + return true; + } + + #isInvalidPosition(position) { + return position < 0 || position >= this.size; + } + + removeAt(position) { + if (this.#size === 0) { + throw new RangeError('Cannot remove from an empty list.'); + } + if (this.#isInvalidPosition(position)) { + throw new RangeError('Invalid position'); + } + if (position === 0) { // Handle removal at head + return this.#removeFromHead(); + } + return this.#removeFromMiddleOrEnd(position); + } + + #removeFromHead() { + const nodeToRemove = this.#head; + this.#head = this.#head.next; + this.#size--; + return nodeToRemove.data; + } + + #removeFromMiddleOrEnd(position) { + let nodeToRemove = this.#head; + let previous; + + for (let index = 0; index < position; index++) { // traverse to the position + previous = nodeToRemove; + nodeToRemove = nodeToRemove.next; + } + + previous.next = nodeToRemove.next; // Unlink the node to be removed + this.#size--; + return nodeToRemove.data; // Return removed data + } + + remove(data, compareFunction = (a, b) => a === b) { + const index = this.indexOf(data, compareFunction); + if (index === -1) { + return null; + } + return this.removeAt(index); + } + + indexOf(data, compareFunction = (a, b) => a === b) { + let current = this.#head; + let index = 0; + while (current) { + if (compareFunction(current.data, data)) { + return index; + } + index++; + current = current.next; + } + return -1; + } + + isEmpty() { + return this.#size === 0; + } + + clear() { + this.#head = null; + this.#size = 0; + } + + get size() { + return this.#size; + } + + toString() { + let current = this.#head; + let objString = ''; + while (current) { + objString += this.#elementToString(current.data); + current = current.next; + if (current) { + objString += ', '; + } + } + return objString; + } + + #elementToString(data) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + reverse() { + let current = this.#head; + let newHead; + let nextNode; + while (current) { + nextNode = current.next; + current.next = newHead; + newHead = current; + current = nextNode; + } + this.#head = newHead; + } +} + +module.exports = LinkedList; \ No newline at end of file diff --git a/src/06-linked-list/media-player.js b/src/06-linked-list/media-player.js new file mode 100644 index 00000000..b67ba703 --- /dev/null +++ b/src/06-linked-list/media-player.js @@ -0,0 +1,141 @@ +class MediaPlayerSong { + constructor(songTitle) { + this.songTitle = songTitle; + this.previous = null; + this.next = null; + } +} + +class MediaPlayer { + #firstSong; + #lastSong; + #size = 0; + #playingSong; + + addSongByTitle(newSongTitle) { + const newSong = new MediaPlayerSong(newSongTitle); + if (this.#size === 0) { // empty list + this.#insertEmptyPlayList(newSong); + } else { + const position = this.#findIndexOfSortedSong(newSongTitle); + if (position === 0) { // insert at the beginning + this.#insertAtBeginning(newSong); + } else if (position === this.#size) { // insert at the end + this.#insertAtEnd(newSong); + } else { // insert in the middle + this.#insertInMiddle(newSong, position); + } + } + this.#size++; + } + + #insertEmptyPlayList(newSong) { + this.#firstSong = newSong; + this.#lastSong = newSong; + newSong.next = newSong; // points to itself + newSong.previous = newSong; // points to itself + } + + #insertAtBeginning(newSong) { + newSong.next = this.#firstSong; + newSong.previous = this.#lastSong; + this.#firstSong.previous = newSong; + this.#lastSong.next = newSong; + this.#firstSong = newSong; + } + + #insertAtEnd(newSong) { + newSong.next = this.#firstSong; + newSong.previous = this.#lastSong; + this.#lastSong.next = newSong; + this.#firstSong.previous = newSong; + this.#lastSong = newSong; + } + + #insertInMiddle(newSong, position) { + let currentSong = this.#firstSong; + for (let i = 0; i < position - 1; i++) { + currentSong = currentSong.next; + } + newSong.next = currentSong.next; + newSong.previous = currentSong; + currentSong.next.previous = newSong; + currentSong.next = newSong; + } + + #findIndexOfSortedSong(newSongTitle) { + let currentSong = this.#firstSong; + let i = 0; + for (; i < this.#size && currentSong; i++) { + const currentSongTitle = currentSong.songTitle; + if (this.#compareSongs(currentSongTitle, newSongTitle) >= 0) { + return i; + } + currentSong = currentSong.next; + } + return 0; + } + + #compareSongs(songTitle1, songTitle2) { + return songTitle1.localeCompare(songTitle2); + } + + play() { + if (this.#size === 0) { + return null; + } + this.#playingSong = this.#firstSong; + return this.#playingSong.songTitle; + } + + next() { + if (this.#size === 0) { + return null; + } + if (!this.#playingSong) { + return this.play(); + } + this.#playingSong = this.#playingSong.next; + return this.#playingSong.songTitle; + } + + previous() { + if (this.#size === 0) { + return null; + } + if (!this.#playingSong) { + return this.play(); + } + this.#playingSong = this.#playingSong.previous; + return this.#playingSong.songTitle; + } + + showPlaylist() { + let currentSong = this.#firstSong; + let result = ''; + for (let i = 0; i < this.#size - 1; i++) { + result += currentSong.songTitle + ' -> '; + currentSong = currentSong.next; + } + result += currentSong.songTitle; + return result; + } +} + +const mediaPlayer = new MediaPlayer(); +mediaPlayer.addSongByTitle('The Bard\'s Song'); +mediaPlayer.addSongByTitle('Florida!!!'); +mediaPlayer.addSongByTitle('Run to the Hills'); +mediaPlayer.addSongByTitle('Nothing Else Matters'); + +console.log('Playing:', mediaPlayer.play()); // Florida!!! +console.log('Next:', mediaPlayer.next()); // Nothing Else Matters +console.log('Next:', mediaPlayer.next()); // Run to the Hills +console.log('Next:', mediaPlayer.next()); // The Bard's Song +console.log('Next:', mediaPlayer.next()); // Florida!!! +console.log('Previous:', mediaPlayer.previous()); // The Bard's Song +console.log('Previous:', mediaPlayer.previous()); // Run to the Hills +console.log('Previous:', mediaPlayer.previous()); // Nothing Else Matters +console.log('Previous:', mediaPlayer.previous()); // Florida!!! + +// to see the output of this file use the command: node src/06-linked-list/media-player.js \ No newline at end of file From b3ec0e1a6788e56cac28a76356935cfb86489675 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 7 Jul 2024 10:50:07 -0400 Subject: [PATCH 30/43] chapter 1: code updated due to review --- src/01-intro/01-hello-variables.js | 52 +++++++++++++++++++++--------- src/01-intro/02-conditionals.js | 3 ++ src/01-intro/04-functions.js | 2 +- src/01-intro/05-scope.js | 29 ++++++++++------- src/01-intro/06-objects.js | 31 +++++++++++------- 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/01-intro/01-hello-variables.js b/src/01-intro/01-hello-variables.js index 59b5f3b0..9dabae22 100644 --- a/src/01-intro/01-hello-variables.js +++ b/src/01-intro/01-hello-variables.js @@ -4,30 +4,52 @@ console.log('Hello, World!'); // variables -var num = 1; // {1} -num = 3; // {2} +var num = 1; +num = 'one' ; -let myVar = 2; // {3} -myVar = 4; // {4} +let myVar = 2; +myVar = 4; -const price = 1.5; // {5} number -const publisher = 'Packt'; // {6} string -const javaScriptBook = true; // {7} boolean -const nullVar = null; // {8} null -let und; // {9} undefined +const price = 1.5; // number +const publisher = 'Packt'; // string +const javaScriptBook = true; // boolean +const nullVar = null; // null +let und; // undefined -console.log('num: ' + num); -console.log('myVar: ' + myVar); +console.log('price: ' + price); console.log('publisher: ' + publisher); console.log('javaScriptBook: ' + javaScriptBook); -console.log('price: ' + price); console.log('nullVar: ' + nullVar); console.log('und: ' + und); +console.log('**** Data types ****'); + +console.log('typeof price: ', typeof price); // number +console.log('typeof publisher: ', typeof publisher); // string +console.log('typeof javaScriptBook: ', typeof javaScriptBook); // boolean +console.log('typeof nullVar: ', typeof nullVar); // object +console.log('typeof und: ', typeof und); // undefined + const book = { - title: 'Data Structures and Algorithms', // {10} + title: 'Data Structures and Algorithms', } -book.title = 'Data Structures and Algorithms in JavaScript'; // {11} -// book = { anotherTitle: 'Data Structures’ } // this will not work {12} + +console.log('book title: ', book.title); + +book.title = 'Data Structures and Algorithms in JavaScript'; +// book = { anotherTitle: 'Data Structures’ } // this will not work + +// reassignment of objects: +let book2 = { + title: 'Data Structures and Algorithms', +} +book2 = { title: 'Data Structures' }; + +// symbol +const title = Symbol('title'); +const book3 = { + [title]: 'Data Structures and Algorithms' +}; +console.log(book3[title]); // Data Structures and Algorithms // to see the output of this file use the command: node src/01-intro/01-hello-variables.js \ No newline at end of file diff --git a/src/01-intro/02-conditionals.js b/src/01-intro/02-conditionals.js index edc9b7bb..83dccb3a 100644 --- a/src/01-intro/02-conditionals.js +++ b/src/01-intro/02-conditionals.js @@ -18,6 +18,9 @@ if (number === 1) { // is the same as number === 1 ? number-- : number++; +// is the same as +number = number === 1 ? number - 1 : number + 1; + /* Example 03 - if-else-if-else... */ let month = 5; if (month === 1) { diff --git a/src/01-intro/04-functions.js b/src/01-intro/04-functions.js index c49b34a4..fbc694c8 100644 --- a/src/01-intro/04-functions.js +++ b/src/01-intro/04-functions.js @@ -11,7 +11,7 @@ function sum(num1, num2) { return num1 + num2; } -var result = sum(1, 2); +const result = sum(1, 2); console.log(result); // outputs 3 /* function with default parameter */ diff --git a/src/01-intro/05-scope.js b/src/01-intro/05-scope.js index 038282c1..4be02631 100644 --- a/src/01-intro/05-scope.js +++ b/src/01-intro/05-scope.js @@ -1,34 +1,39 @@ // Path: src/01-intro/05-scope.js -let movie = 'Lord of the Rings'; // {1} +let movie = 'Lord of the Rings'; function starWarsFan() { - const movie = 'Star Wars'; // {2} + const movie = 'Star Wars'; return movie; } function marvelFan() { - movie = 'The Avengers'; // {3} + movie = 'The Avengers'; return movie; } +console.log(movie); // Lord of the Rings +console.log(starWarsFan()); // Star Wars +console.log(marvelFan()); // The Avengers +console.log(movie); // The Avengers + +// block scope function blizzardFan() { const isFan = true; - let phrase = 'Warcraft'; // {4} + let phrase = 'Warcraft'; console.log('Before if: ' + phrase); if (isFan) { - let phrase = 'initial text'; // {5} - phrase = 'For the Horde!'; // {6} + let phrase = 'initial text'; + phrase = 'For the Horde!'; console.log('Inside if: ' + phrase); } - phrase = 'For the Alliance!'; // {7} + phrase = 'For the Alliance!'; console.log('After if: ' + phrase); } -console.log(movie); // Lord of the Rings -console.log(starWarsFan()); // Star Wars -console.log(marvelFan()); // The Avengers -console.log(movie); // The Avengers -blizzardFan(); // Before if: Warcraft, Inside if: For the Horde!, After if: For the Alliance! +blizzardFan(); +// Before if: Warcraft +// Inside if: For the Horde! +// After if: For the Alliance! // to see the output of this file use the command: node src/01-intro/05-scope.js diff --git a/src/01-intro/06-objects.js b/src/01-intro/06-objects.js index 17c8d6d3..511459e4 100644 --- a/src/01-intro/06-objects.js +++ b/src/01-intro/06-objects.js @@ -15,24 +15,24 @@ obj = { /* Class example */ class Book { - #percentagePerSale = 0.12; // {1} + #percentagePerSale = 0.12; constructor(title, pages, isbn) { - this.title = title; // {2} + this.title = title; this.pages = pages; this.isbn = isbn; } - get price() { // {3} + get price() { return this.pages * this.#percentagePerSale; } - static copiesSold = 0; // {4} - static sellCopy() { // {5} + static copiesSold = 0; + static sellCopy() { this.copiesSold++; } - printIsbn() { // {6} + printIsbn() { console.log(this.isbn); } } @@ -52,13 +52,22 @@ console.log(Book.copiesSold); // 1 Book.sellCopy(); console.log(Book.copiesSold); // 2 -class Ebook extends Book { // {7} +class Ebook extends Book { constructor(title, pages, isbn, format) { - super(title, pages, isbn); // {8} - this.format = format; // {9} + super(title, pages, isbn); + this.format = format; + } + printIsbn() { + console.log('Ebook ISBN:',this.isbn); } } -Ebook.sellCopy(); // {10} -console.log(Ebook.copiesSold); // {11} 3 +Ebook.sellCopy(); +console.log(Ebook.copiesSold); // 3 + +const myBook = new Book('title', 400, 'isbn'); +myBook.printIsbn(); // isbn +const myEbook = new Ebook('Data Structures Ebook', 400, 'isbn 123', 'pdf'); +myEbook.printIsbn(); // Ebook ISBN: isbn 123 + // to see the output of this file use the command: node src/01-intro/06-objects.js From 804bb7c3dd2768333a4c8518218543cf4805840f Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 12 Jul 2024 14:54:57 -0400 Subject: [PATCH 31/43] chapter 6: fixed tests --- .../__test__/doubly-linked-list.test.ts | 12 ++++++------ ...cular-linked-list.js => circular-linked-list_.js} | 2 +- ...{doubly-linked-list.js => doubly-linked-list_.js} | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/06-linked-list/{circular-linked-list.js => circular-linked-list_.js} (99%) rename src/06-linked-list/{doubly-linked-list.js => doubly-linked-list_.js} (96%) diff --git a/src/06-linked-list/__test__/doubly-linked-list.test.ts b/src/06-linked-list/__test__/doubly-linked-list.test.ts index 2473c527..b8a1110e 100644 --- a/src/06-linked-list/__test__/doubly-linked-list.test.ts +++ b/src/06-linked-list/__test__/doubly-linked-list.test.ts @@ -29,14 +29,14 @@ describe('DoublyLinkedList', () => { test('should insert node at position 0', () => { doublyLinkedList.append(1); - doublyLinkedList.insert(0, 2); + doublyLinkedList.insert(2, 0); expect(doublyLinkedList.toString()).toBe('2, 1'); }); test('should insert node at given position', () => { doublyLinkedList.append(1); doublyLinkedList.append(3); - doublyLinkedList.insert(1, 2); + doublyLinkedList.insert(2, 1); expect(doublyLinkedList.toString()).toBe('1, 2, 3'); }); @@ -64,9 +64,9 @@ describe('DoublyLinkedList', () => { test('should remove node at invalid position', () => { doublyLinkedList.append(1); - doublyLinkedList.append(3); - expect(doublyLinkedList.removeAt(3)).toBe(false); - expect(doublyLinkedList.toString()).toBe('1, 3'); + doublyLinkedList.append(2); + //expect(doublyLinkedList.removeAt(3)).toThrowError(RangeError('Invalid position')); + expect(doublyLinkedList.toString()).toBe('1, 2'); }); test('should remove element from doubly linked list', () => { @@ -84,7 +84,7 @@ describe('DoublyLinkedList', () => { test('should remove element that is not in doubly linked list', () => { doublyLinkedList.append(1); doublyLinkedList.append(2); - expect(doublyLinkedList.remove(3)).toBe(false); + expect(doublyLinkedList.remove(3)).toBe(null); expect(doublyLinkedList.toString()).toBe('1, 2'); }); diff --git a/src/06-linked-list/circular-linked-list.js b/src/06-linked-list/circular-linked-list_.js similarity index 99% rename from src/06-linked-list/circular-linked-list.js rename to src/06-linked-list/circular-linked-list_.js index 62ea7f46..e85f3888 100644 --- a/src/06-linked-list/circular-linked-list.js +++ b/src/06-linked-list/circular-linked-list_.js @@ -190,4 +190,4 @@ class CircularLinkedList { } } -export default CircularLinkedList; +module.exports = CircularLinkedList; diff --git a/src/06-linked-list/doubly-linked-list.js b/src/06-linked-list/doubly-linked-list_.js similarity index 96% rename from src/06-linked-list/doubly-linked-list.js rename to src/06-linked-list/doubly-linked-list_.js index 32a4fef7..68e0a98b 100644 --- a/src/06-linked-list/doubly-linked-list.js +++ b/src/06-linked-list/doubly-linked-list_.js @@ -173,7 +173,7 @@ class DoublyLinkedList { let current = this.#head; let objString = ''; while (current) { - objString += this.elementToString(current.data); + objString += this.#elementToString(current.data); current = current.next; if (current) { objString += ', '; @@ -187,13 +187,13 @@ class DoublyLinkedList { let current = this.#tail; let objString = ''; while (current) { - objString += this.elementToString(current.data); + objString += this.#elementToString(current.data); current = current.previous; } return objString; } - private elementToString(data: T): string { + #elementToString(data) { if (typeof data === 'object' && data !== null) { return JSON.stringify(data); } else { @@ -217,4 +217,4 @@ class DoublyLinkedList { } } -export default DoublyLinkedList; \ No newline at end of file +module.exports = DoublyLinkedList; \ No newline at end of file From fe6f722f8cf4ee6083f891f8dc388d9db3312413 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 14 Jul 2024 21:13:16 -0400 Subject: [PATCH 32/43] chapter 07: sets --- src/07-set/01-using-myset-class.js | 145 ++++++++++++++++++ src/07-set/02-using-set-class.js | 29 ++++ .../remove-duplicates-from-sorted-array.ts | 36 +++++ src/07-set/set.js | 105 +++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 src/07-set/01-using-myset-class.js create mode 100644 src/07-set/02-using-set-class.js create mode 100644 src/07-set/leetcode/remove-duplicates-from-sorted-array.ts create mode 100644 src/07-set/set.js diff --git a/src/07-set/01-using-myset-class.js b/src/07-set/01-using-myset-class.js new file mode 100644 index 00000000..ec4722dc --- /dev/null +++ b/src/07-set/01-using-myset-class.js @@ -0,0 +1,145 @@ +// src/07-set/01-using-myset-class.js + +const MySet = require('./set'); + +const article = { + title: 'The importance of data structures in programming', + content: '...', + tags: new MySet() // using MySet to store tags +}; + +// add tags +article.tags.add('programming'); +article.tags.add('data structures'); +article.tags.add('algorithms'); +article.tags.add('programming'); + +console.log(article.tags.size); // 3 +console.log(article.tags.has('data structures')); // true +console.log(article.tags.has('algorithms')); // true +console.log(article.tags.has('programming')); // true +console.log(article.tags.has('javascript')); // false +console.log(article.tags.values()); // ['programming', 'data structures', 'algorithms'] + +// remove tags +article.tags.delete('programming'); +article.tags.add('JavaScript'); +console.log(article.tags.values()); // ['data structures', 'algorithms', 'JavaScript'] + +// union example +const interestsFromWebsites = new MySet(); +interestsFromWebsites.addAll(['technology', 'politics', 'photography']); + +const interestsFromSocialMedia = new MySet(); +interestsFromSocialMedia.addAll(['technology', 'movies', 'books']); + +const allInterests = interestsFromWebsites.union(interestsFromSocialMedia); +console.log(allInterests.values()); // ['technology', 'politics', 'photography', 'movies', 'books'] + +// intersection example +const job1Skills = new MySet(); +job1Skills.addAll(['JavaScript', 'Angular', 'Java', 'SQL']); +const job2Skills = new MySet(); +job2Skills.addAll(['Python', 'Machine Learning', 'SQL', 'Statistics']); +const jobPostings = + [{ + title: 'Software Engineer', + skills: job1Skills + }, + { + title: 'Data Scientist', + skills: job2Skills + }]; + +const candidateSkills = new MySet(); +candidateSkills.addAll(['JavaScript', 'Angular', 'TypeScript', 'AWS']); +const candidate = { + name: 'Loiane', + skills: candidateSkills +}; + +// Function to match candidate with jobs +function matchCandidateWithJobs(candidate, jobPostings) { + const matches = []; + for (const job of jobPostings) { + const matchingSkillsSet = candidate.skills.intersection(job.skills); + if (!matchingSkillsSet.isEmpty()){ + matches.push({ + title: job.title, + matchingSkills: matchingSkillsSet.values() + }); + } + } + return matches; +} + +// Find matching jobs for the candidate +const matchingJobs = matchCandidateWithJobs(candidate, jobPostings); +console.log(matchingJobs); +// output: [{ title: 'Software Engineer', matchingSkills: [ 'JavaScript', 'Angular' ] }] + +// difference example +// Sets representing subscriber interests +const allSubscribers = new MySet(); +allSubscribers.addAll(['Aelin', 'Rowan', 'Xaden', 'Poppy', 'Violet']); +const booksInterested = new MySet(); +booksInterested.addAll(['Aelin', 'Poppy', 'Violet']); +const alreadyPurchasedBooks = new MySet(); +alreadyPurchasedBooks.addAll(['Poppy']); + +// Find subscribers interested in books but haven't purchased yet +const targetSubscribers = booksInterested.difference(alreadyPurchasedBooks); + +// Send targeted email +targetSubscribers.values().forEach(subscriber => { + sendEmail(subscriber, 'New books you will love!'); +}); + +function sendEmail(subscriber, message) { + console.log(`Sending email to ${subscriber}: ${message}`); +} +// Sending email to Aelin: New books you will love! +// Sending email to Violet: New books you will love! + +// subset example +// Example Recipe Data + +const chickenIngredients = new MySet() +chickenIngredients.addAll(['chicken', 'tomato', 'onion', 'garlic', 'ginger', 'spices']); + +const spaghettiIngredients = new MySet(); +spaghettiIngredients.addAll(['spaghetti', 'eggs', 'bacon', 'parmesan', 'pepper']); + +const recipes = +[{ + name: 'Chicken Tikka Masala', + ingredients: chickenIngredients + }, + { + name: 'Spaghetti Carbonara', + ingredients: spaghettiIngredients + }]; + +// User's available ingredients +const userIngredients = new MySet(); +userIngredients.addAll(['chicken', 'onion', 'garlic', 'ginger']); + +// Function to filter recipes +function filterRecipes(recipes, userIngredients) { + const filteredRecipes = []; + for (const recipe of recipes) { + if (userIngredients.isSubsetOf(recipe.ingredients)) { + filteredRecipes.push({ name: recipe.name }); + } + } + return filteredRecipes; +} + +// Filter recipes based on user's ingredients +const matchingRecipes = filterRecipes(recipes, userIngredients); +console.log(matchingRecipes); +// [ { name: 'Chicken Tikka Masala' } ] + + + +// to see the output of this file use the command: node src/07-set/01-using-myset-class.js \ No newline at end of file diff --git a/src/07-set/02-using-set-class.js b/src/07-set/02-using-set-class.js new file mode 100644 index 00000000..4756b2f0 --- /dev/null +++ b/src/07-set/02-using-set-class.js @@ -0,0 +1,29 @@ +// src/07-set/02-using-set-class.js + +const article = { + title: 'The importance of data structures in programming', + content: '...', + tags: new Set() + tags: new Set(['programming', 'data structures', 'algorithms']) +}; + +// add tags +article.tags.add('programming'); +article.tags.add('data structures'); +article.tags.add('algorithms'); +article.tags.add('programming'); + +console.log(article.tags.size); // 3 +console.log(article.tags.has('data structures')); // true +console.log(article.tags.has('algorithms')); // true +console.log(article.tags.has('programming')); // true +console.log(article.tags.has('javascript')); // false +console.log(article.tags.values()); // ['programming', 'data structures', 'algorithms'] + +// remove tags +article.tags.delete('programming'); +article.tags.add('JavaScript'); +console.log(article.tags.values()); // ['data structures', 'algorithms', 'JavaScript'] + + +// to see the output of this file use the command: node src/07-set/02-using-set-class.js \ No newline at end of file diff --git a/src/07-set/leetcode/remove-duplicates-from-sorted-array.ts b/src/07-set/leetcode/remove-duplicates-from-sorted-array.ts new file mode 100644 index 00000000..56f62d53 --- /dev/null +++ b/src/07-set/leetcode/remove-duplicates-from-sorted-array.ts @@ -0,0 +1,36 @@ +// https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/ + +/** + * @param {number[]} nums + * @return {number} + */ +export function removeDuplicates(nums: number[]): number { + if (nums.length === 0) { + return 0; + } + let i = 0; + for (let j = 1; j < nums.length; j++) { + if (nums[j] !== nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; +} + +// resolve this problem using Set +export function removeDuplicates2(nums: number[]): number { + const set = new Set(nums); + const arr = Array.from(set); + for (let i = 0; i < arr.length; i++) { + nums[i] = arr[i]; + } + return arr.length; +} + +// explain above solution +// 1. create a set from the array +// 2. create an array from the set +// 3. copy the array to the original array +// 4. return the length of the array +// Time complexity: O(n) \ No newline at end of file diff --git a/src/07-set/set.js b/src/07-set/set.js new file mode 100644 index 00000000..f681a8dc --- /dev/null +++ b/src/07-set/set.js @@ -0,0 +1,105 @@ +class MySet { + #items = {}; + #size = 0; + + add(value) { + if (!this.has(value)) { + this.#items[value] = true; // mark the value as present + this.#size++; + return true; + } + return false; + } + + addAll(values) { + values.forEach(value => this.add(value)); + } + + delete(value) { + if (this.has(value)) { + delete this.#items[value]; + this.#size--; + return true; + } + return false; + } + + has(value) { + return this.#items.hasOwnProperty(value); + } + + values() { + return Object.keys(this.#items); + } + + get size() { + return this.#size; + } + + getSizeWithoutSizeProperty() { + let count = 0; + for (const key in this.#items) { + if (this.#items.hasOwnProperty(key)) { + count++; + } + } + return count; + } + + isEmpty() { + return this.#size === 0; + } + + clear() { + this.#items = {}; + this.#size = 0; + } + + union(otherSet) { + const unionSet = new MySet(); + this.values().forEach(value => unionSet.add(value)); + otherSet.values().forEach(value => unionSet.add(value)); + return unionSet; + } + + intersection(otherSet) { + const intersectionSet = new MySet(); + const [smallerSet, largerSet] = this.size <= otherSet.size ? [this, otherSet] : [otherSet, this]; + smallerSet.values().forEach(value => { + if (largerSet.has(value)) { + intersectionSet.add(value); + } + }); + return intersectionSet; + } + + difference(otherSet) { + const differenceSet = new MySet(); + this.values().forEach(value => { + if (!otherSet.has(value)) { + differenceSet.add(value); + } + }); + return differenceSet; + } + + isSubsetOf(otherSet) { + if (this.size > otherSet.size) { + return false; + } + return this.values().every(value => otherSet.has(value)); + } + + isSupersetOf(otherSet) { + if (this.size < otherSet.size) { + return false; + } + return otherSet.values().every(value => this.has(value)); + } + + toString() { + return this.values().join(', '); + } +} + +module.exports = MySet; From 446bbab43fc170f71b8d230a99db17ba52b4dc41 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 25 Jul 2024 16:45:28 -0400 Subject: [PATCH 33/43] chapter 8: dictionary and hash table --- src/06-linked-list/linked-list_.js | 10 ++ .../01-using-dictionary-class.js | 42 ++++++++ src/08-dictionary-hash/02-using-map-class.js | 40 +++++++ .../03-using-weakmap-class.js | 19 ++++ .../04-using-hashmap-class.js | 26 +++++ .../05-hashmap-collision.js | 42 ++++++++ src/08-dictionary-hash/dictionary.js | 59 ++++++++++ .../hash-table-linear-probing.js | 101 ++++++++++++++++++ .../hash-table-separate-chaining.js | 83 ++++++++++++++ src/08-dictionary-hash/hash-table.js | 67 ++++++++++++ src/08-dictionary-hash/hash-table.ts | 62 +++++++++++ .../leetcode/integer-to-roman.ts | 38 +++++++ src/08-dictionary-hash/leetcode/two-sum.ts | 30 ++++++ 13 files changed, 619 insertions(+) create mode 100644 src/08-dictionary-hash/01-using-dictionary-class.js create mode 100644 src/08-dictionary-hash/02-using-map-class.js create mode 100644 src/08-dictionary-hash/03-using-weakmap-class.js create mode 100644 src/08-dictionary-hash/04-using-hashmap-class.js create mode 100644 src/08-dictionary-hash/05-hashmap-collision.js create mode 100644 src/08-dictionary-hash/dictionary.js create mode 100644 src/08-dictionary-hash/hash-table-linear-probing.js create mode 100644 src/08-dictionary-hash/hash-table-separate-chaining.js create mode 100644 src/08-dictionary-hash/hash-table.js create mode 100644 src/08-dictionary-hash/hash-table.ts create mode 100644 src/08-dictionary-hash/leetcode/integer-to-roman.ts create mode 100644 src/08-dictionary-hash/leetcode/two-sum.ts diff --git a/src/06-linked-list/linked-list_.js b/src/06-linked-list/linked-list_.js index 18ef5586..91ec6345 100644 --- a/src/06-linked-list/linked-list_.js +++ b/src/06-linked-list/linked-list_.js @@ -126,6 +126,16 @@ class LinkedList { return this.#size; } + forEach(callback) { + let current = this.#head; + let index = 0; + while (current) { + callback(current.data, index); + current = current.next; + index++; + } + } + toString() { let current = this.#head; let objString = ''; diff --git a/src/08-dictionary-hash/01-using-dictionary-class.js b/src/08-dictionary-hash/01-using-dictionary-class.js new file mode 100644 index 00000000..9dab635b --- /dev/null +++ b/src/08-dictionary-hash/01-using-dictionary-class.js @@ -0,0 +1,42 @@ +// src/08-dictionary-hash/01-using-dictionary-class.js + +const Dictionary = require('./dictionary'); + +const translations = new Dictionary(); + +// Add some translations - English to Portuguese +translations.set("hello", "olΓ‘"); +translations.set("thank you", "obrigado"); +translations.set("book", "livro"); +translations.set("cat", "gato"); +translations.set("computer", "computador"); + +// User interaction +function translateWord(word) { + if (translations.hasKey(word)) { + const translation = translations.get(word); + console.log(`The translation of "${word}" is "${translation}"`); + } else { + console.log(`Sorry, no translation found for "${word}"`); + } +} + +// Example usage +translateWord("hello"); // Output: The translation of "hello" is "olΓ‘" +translateWord("dog"); // Output: Sorry, no translation found for "dog" + +// Get all translations +console.log("All translations:", translations.values()); +// All translations: [ 'olΓ‘', 'obrigado', 'livro', 'gato', 'computador' ] + +// Get all words +console.log("All words:", translations.keys()); +// All words: [ 'hello', 'thank you', 'book', 'cat', 'computer' ] + +// Iterate through all translations +translations.forEach((value, key) => { + console.log(`${key}: ${value}`); +}); + + +// to see the output of this file use the command: node src/08-dictionary-hash/01-using-dictionary-class.js \ No newline at end of file diff --git a/src/08-dictionary-hash/02-using-map-class.js b/src/08-dictionary-hash/02-using-map-class.js new file mode 100644 index 00000000..28a904fb --- /dev/null +++ b/src/08-dictionary-hash/02-using-map-class.js @@ -0,0 +1,40 @@ +// src/08-dictionary-hash/02-using-map-class.js + +const translations = new Map(); + +// Add some translations - English to Portuguese +translations.set("hello", "olΓ‘"); +translations.set("thank you", "obrigado"); +translations.set("book", "livro"); +translations.set("cat", "gato"); +translations.set("computer", "computador"); + +// User interaction +function translateWord(word) { + if (translations.has(word)) { + const translation = translations.get(word); + console.log(`The translation of "${word}" is "${translation}"`); + } else { + console.log(`Sorry, no translation found for "${word}"`); + } +} + +// Example usage +translateWord("hello"); // Output: The translation of "hello" is "olΓ‘" +translateWord("dog"); // Output: Sorry, no translation found for "dog" + +// Get all translations +console.log("All translations:", translations.values()); +// All translations: [ 'olΓ‘', 'obrigado', 'livro', 'gato', 'computador' ] + +// Get all words +console.log("All words:", translations.keys()); +// All words: [ 'hello', 'thank you', 'book', 'cat', 'computer' ] + +// Iterate through all translations +translations.forEach((value, key) => { + console.log(`${key}: ${value}`); +}); + + +// to see the output of this file use the command: node src/08-dictionary-hash/01-using-dictionary-class.js \ No newline at end of file diff --git a/src/08-dictionary-hash/03-using-weakmap-class.js b/src/08-dictionary-hash/03-using-weakmap-class.js new file mode 100644 index 00000000..49a8a494 --- /dev/null +++ b/src/08-dictionary-hash/03-using-weakmap-class.js @@ -0,0 +1,19 @@ +const privateData = new WeakMap(); + +class Person { + constructor(name, age) { + this.name = name; + this.age = age; + privateData.set(this, { ssn: 'XXX-XX-XXXX', medicalHistory: [] }); + } + + getSSN() { + return privateData.get(this)?.ssn; + } +} + +const alice = new Person("Penelope", 20); +console.log(alice.name); // Penelope +console.log(alice.getSSN()); // XXX-XX-XXXX + +// Try to access private data \ No newline at end of file diff --git a/src/08-dictionary-hash/04-using-hashmap-class.js b/src/08-dictionary-hash/04-using-hashmap-class.js new file mode 100644 index 00000000..7e727871 --- /dev/null +++ b/src/08-dictionary-hash/04-using-hashmap-class.js @@ -0,0 +1,26 @@ +// src/08-dictionary-hash/04-using-hashmap-class.js + +const HashTable = require('./hash-table'); + +const addressBook = new HashTable(); + +// Add contacts +addressBook.put('Gandalf', 'gandalf@email.com'); +addressBook.put('John', 'johnsnow@email.com'); +addressBook.put('Tyrion', 'tyrion@email.com'); + +// Retrieve the hash code of a contact +console.log(addressBook.hash('Gandalf')); // 19 +console.log(addressBook.hash('John')); // 29 +console.log(addressBook.hash('Tyrion')); // 16 + +// Retrieve contacts +console.log(addressBook.get('Gandalf')); // gandalf@email.com +console.log(addressBook.get('Loiane')); // undefined + +// Remove contacts +console.log(addressBook.remove('Gandalf')); // true +console.log(addressBook.get('Gandalf')); // undefined + + +// to see the output of this file use the command: node src/08-dictionary-hash/04-using-hashmap-class.js \ No newline at end of file diff --git a/src/08-dictionary-hash/05-hashmap-collision.js b/src/08-dictionary-hash/05-hashmap-collision.js new file mode 100644 index 00000000..3a020ca3 --- /dev/null +++ b/src/08-dictionary-hash/05-hashmap-collision.js @@ -0,0 +1,42 @@ +// src/08-dictionary-hash/05-hashmap-collision.js + +const HashTable = require('./hash-table'); + +const addressBook = new HashTable(); + +// Add contacts +addressBook.put('Ygritte', 'ygritte@email.com'); +addressBook.put('Jonathan', 'jonathan@email.com'); +addressBook.put('Jamie', 'jamie@email.com'); +addressBook.put('Jack', 'jack@email.com'); +addressBook.put('Jasmine', 'jasmine@email.com'); +addressBook.put('Jake', 'jake@email.com'); +addressBook.put('Nathan', 'nathan@email.com'); +addressBook.put('Athelstan', 'athelstan@email.com'); +addressBook.put('Sue', 'sue@email.com'); +addressBook.put('Aethelwulf', 'aethelwulf@email.com'); +addressBook.put('Sargeras', 'sargeras@email.com'); + +// Retrieve the hash code of a contact +console.log(addressBook.hash('Ygritte'), '- Ygritte'); // 4 +console.log(addressBook.hash('Jonathan'), '- Jonathan'); // 5 +console.log(addressBook.hash('Jamie'), '- Jamie'); // 5 +console.log(addressBook.hash('Jack'), '- Jack'); // 7 +console.log(addressBook.hash('Jasmine'), '- Jasmine'); // 8 +console.log(addressBook.hash('Jake'), '- Jake'); // 9 +console.log(addressBook.hash('Nathan'), '- Nathan'); // 10 +console.log(addressBook.hash('Athelstan'), '- Athelstan'); // 7 +console.log(addressBook.hash('Sue'), '- Sue'); // 5 +console.log(addressBook.hash('Aethelwulf'), '- Aethelwulf'); // 5 +console.log(addressBook.hash('Sargeras'), '- Sargeras'); // 10 + +console.log(addressBook.toString()); +// {4 => ygritte@email.com} +// {5 => aethelwulf@email.com} +// {7 => athelstan@email.com} +// {8 => jasmine@email.com} +// {9 => jake@email.com} +// {10 => sargeras@email.com} + + +// to see the output of this file use the command: node src/08-dictionary-hash/05-hashmap-collision.js \ No newline at end of file diff --git a/src/08-dictionary-hash/dictionary.js b/src/08-dictionary-hash/dictionary.js new file mode 100644 index 00000000..47d568eb --- /dev/null +++ b/src/08-dictionary-hash/dictionary.js @@ -0,0 +1,59 @@ +// src/08-dictionary-hash/dictionary.js + +class Dictionary { + #items = {}; + #size = 0; + + hasKey(key) { + return this.#items[this.#elementToString(key)] != null; + } + + set(key, value) { + if (key != null && value != null) { + const tableKey = this.#elementToString(key); + this.#items[tableKey] = value; + this.#size++; + return true; + } + return false; + } + + delete(key) { + if (this.hasKey(key)) { + delete this.#items[this.#elementToString(key)]; + this.#size--; + return true; + } + return false; + } + + get(key) { + return this.#items[this.#elementToString(key)]; + } + + values() { + return Object.values(this.#items); + } + + keys() { + return Object.keys(this.#items); + } + + forEach(callbackFn) { + for (const key in this.#items) { + if (this.#items.hasOwnProperty(key)) { + callbackFn(this.#items[key], key); + } + } + } + + #elementToString(data) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } +} + +module.exports = Dictionary; \ No newline at end of file diff --git a/src/08-dictionary-hash/hash-table-linear-probing.js b/src/08-dictionary-hash/hash-table-linear-probing.js new file mode 100644 index 00000000..9947d03d --- /dev/null +++ b/src/08-dictionary-hash/hash-table-linear-probing.js @@ -0,0 +1,101 @@ +// src/08-dictionary-hash/hash-table-linear-probing.js + +class HashTableLinearProbing { + #table = []; + + #loseLoseHashCode(key) { + if (typeof key !== 'string') { + key = this.#elementToString(key); + } + const calcASCIIValue = (acc, char) => acc + char.charCodeAt(0); + const hash = key.split('').reduce((acc, char) => calcASCIIValue, 0); + return hash % 37; // mod to reduce the hash code + } + + #djb2HashCode(key) { + if (typeof key !== 'string') { + key = this.#elementToString(key); + } + const calcASCIIValue = (acc, char) => (acc * 33) + char.charCodeAt(0); + const hash = key.split('').reduce((acc, char) => calcASCIIValue, 5381); + return hash % 1013; + } + + hash(key) { + return this.#loseLoseHashCode(key); + } + + #elementToString(data) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + put(key, value) { + if (key != null && value != null) { + let index = this.hash(key); + + // linear probing to find an empty slot + while (this.#table[index] != null) { + if (this.#table[index].key === key) { + this.#table[index].value = value; // update existing key + return true; + } + index++; + index %= this.#table.length; // wrap around if end is reached + } + + this.#table[index] = {key, value}; + return true; + } + return false; + } + + get(key) { + let index = this.hash(key); + + // Linear probing to search for the key + while (this.#table[index] != null) { + if (this.#table[index].key === key) { + return this.#table[index].value; + } + index++; + index %= this.#table.length; + } + + return undefined; // key not found + } + + remove(key) { + let index = this.hash(key); + while (this.#table[index] != null) { + if (this.#table[index].key === key) { + delete this.#table[index]; + this.#verifyRemoveSideEffect(key, index); + return true; + } + index++; + index %= this.#table.length; + } + return false; // key not found + } + + #verifyRemoveSideEffect(key, removedPosition) { + const size = this.#table.length; + let index = removedPosition + 1; + while (this.#table[index] != null) { + const currentKey = this.#table[index].key; + const currentHash = this.hash(currentKey); + // check if the element should be repositioned + if (currentHash <= removedPosition) { + this.#table[removedPosition] = this.#table[index]; + delete this.#table[index]; + removedPosition = index; + } + index++; + index %= size; // Wrap around if end is reached + } + } +} diff --git a/src/08-dictionary-hash/hash-table-separate-chaining.js b/src/08-dictionary-hash/hash-table-separate-chaining.js new file mode 100644 index 00000000..f8567ecf --- /dev/null +++ b/src/08-dictionary-hash/hash-table-separate-chaining.js @@ -0,0 +1,83 @@ +// src/08-dictionary-hash/hash-table-separate-chaining.js + +const LinkedList = require('../06-linked-list/linked-list_'); + +class HashTableSeparateChaining { + #table = []; + + #loseLoseHashCode(key) { + if (typeof key !== 'string') { + key = this.#elementToString(key); + } + const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + return hash % 37; // mod to reduce the hash code + } + + hash(key) { + return this.#loseLoseHashCode(key); + } + + + put(key, value) { + if (key != null && value != null) { + const index = this.hash(key); // Get hash code (index) + + if (this.#table[index] == null) { + this.#table[index] = new LinkedList(); // Create linked list if needed + } + + this.#table[index].append({key, value}); // Append to linked list + return true; + } + return false; + } + + get(key) { + const index = this.hash(key); + const linkedList = this.#table[index]; + if (linkedList != null) { + linkedList.forEach((element) => { + if (element.key === key) { + return element.value; + } + }); + } + return undefined; // key not found + } + + remove(key) { + const index = this.hash(key); + const linkedList = this.#table[index]; + if (linkedList != null) { + const compareFunction = (a, b) => a.key === b.key; + const toBeRemovedIndex = linkedList.indexOf({key}, compareFunction); + if (toBeRemovedIndex >= 0) { + linkedList.removeAt(toBeRemovedIndex); + if (linkedList.isEmpty()) { + this.#table[index] = undefined; + } + return true; + } + } + return false; // key not found + } + + #elementToString(data) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + toString() { + const keys = Object.keys(this.#table); + let objString = `{${keys[0]} => ${this.#table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + const value = this.#elementToString(this.#table[keys[i]]).toString(); + objString = `${objString}\n{${keys[i]} => ${value}}`; + } + return objString; + } + +} diff --git a/src/08-dictionary-hash/hash-table.js b/src/08-dictionary-hash/hash-table.js new file mode 100644 index 00000000..3833eb72 --- /dev/null +++ b/src/08-dictionary-hash/hash-table.js @@ -0,0 +1,67 @@ +// src/08-dictionary-hash/hash-table.js + +class HashTable { + + #table = []; + + #loseLoseHashCode(key) { + if (typeof key !== 'string') { + key = this.#elementToString(key); + } + const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + return hash % 37; // mod to reduce the hash code + } + + hash(key) { + return this.#loseLoseHashCode(key); + } + + put(key, value) { + if (key == null && value == null) { + return false; + } + const index = this.hash(key); + this.#table[index] = value; + return true; + } + + get(key) { + if (key == null) { + return undefined; + } + const index = this.hash(key); + return this.#table[index]; + } + + remove(key) { + if (key == null) { + return false; + } + const index = this.hash(key); + if (this.#table[index]) { + delete this.#table[index]; + return true; + } + return false; + } + + #elementToString(data) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + toString() { + const keys = Object.keys(this.#table); + let objString = `{${keys[0]} => ${this.#table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + const value = this.#elementToString(this.#table[keys[i]]).toString(); + objString = `${objString}\n{${keys[i]} => ${value}}`; + } + return objString; + } +} + +module.exports = HashTable; \ No newline at end of file diff --git a/src/08-dictionary-hash/hash-table.ts b/src/08-dictionary-hash/hash-table.ts new file mode 100644 index 00000000..ce35f904 --- /dev/null +++ b/src/08-dictionary-hash/hash-table.ts @@ -0,0 +1,62 @@ +// src/08-dictionary-hash/hash-table.ts + +class HashTable { + + private table: V[] = []; + + #loseLoseHashCode(key: string) { + // if (typeof key !== 'string') { + // key = this.#elementToString(key); + // } + const calcASCIIValue = (acc, char) => acc + char.charCodeAt(0); + const hash = key.split('').reduce(calcASCIIValue, 0); + return hash % 37; + } + + hash(key: string) { + return this.#loseLoseHashCode(key); + } + + put(key: string, value: V) { + const index = this.hash(key); + this.table[index] = value; + return true; + } + + get(key: string): V { + const index = this.hash(key); + return this.table[index]; + } + + remove(key: string) { + if (key == null) { + return false; + } + const index = this.hash(key); + if (this.table[index]) { + delete this.table[index]; + return true; + } + return false; + } + + #elementToString(data: V) { + if (typeof data === 'object' && data !== null) { + return JSON.stringify(data); + } else { + return data.toString(); + } + } + + toString() { + const keys = Object.keys(this.table); + let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; + for (let i = 1; i < keys.length; i++) { + const value = this.#elementToString(this.table[keys[i]]).toString(); + objString = `${objString}\n{${keys[i]} => ${value}}`; + } + return objString; + } +} + +export default HashTable; \ No newline at end of file diff --git a/src/08-dictionary-hash/leetcode/integer-to-roman.ts b/src/08-dictionary-hash/leetcode/integer-to-roman.ts new file mode 100644 index 00000000..2d87cd43 --- /dev/null +++ b/src/08-dictionary-hash/leetcode/integer-to-roman.ts @@ -0,0 +1,38 @@ +// https://leetcode.com/problems/integer-to-roman/description/ + +// Time complexity: O(1) +function intToRoman(num: number): string { + // define map + const romanMap = { + M:1000, + CM:900, + D:500, + CD:400, + C:100, + XC:90, + L:50, + XL:40, + X:10, + IX:9, + V:5, + IV:4, + I:1 + }; + + let result = ''; + for(let romanNum in romanMap){ + while (num >= romanMap[romanNum]) { + result += romanNum; + num -= romanMap[romanNum]; + } + } + return result; +}; + +// Test cases +console.log(intToRoman(3)); // "III" +console.log(intToRoman(4)); // "IV" +console.log(intToRoman(9)); // "IX" +console.log(intToRoman(58)); // "LVIII" +console.log(intToRoman(1994)); // "MCMXCIV" +console.log(intToRoman(3999)); // "MMMCM" diff --git a/src/08-dictionary-hash/leetcode/two-sum.ts b/src/08-dictionary-hash/leetcode/two-sum.ts new file mode 100644 index 00000000..55638892 --- /dev/null +++ b/src/08-dictionary-hash/leetcode/two-sum.ts @@ -0,0 +1,30 @@ +// https://leetcode.com/problems/two-sum/ + +// Time complexity: O(n) +// Space complexity: O(n) +function twoSum(nums: number[], target: number): number[] { + const map = new Map(); + for (let i = 0; i < nums.length; i++) { + const diff = target - nums[i]; + if (map.has(diff)) { + return [map.get(diff)!, i]; + } + map.set(nums[i], i); + } + return []; +} + +// Test cases +console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] +console.log(twoSum([3, 2, 4], 6)); // [1, 2] +console.log(twoSum([3, 3], 6)); // [0, 1] + +function twoSum2(nums: number[], target: number): number[] { + for (let i=0; i Date: Fri, 26 Jul 2024 17:39:35 -0400 Subject: [PATCH 34/43] chapter 2: changes after feedback --- src/02-bigOnotation/01-big-o-intro.js | 88 +++++++++++++-------------- src/02-bigOnotation/03-exercises.js | 57 +++++++++++++++++ 2 files changed, 99 insertions(+), 46 deletions(-) create mode 100644 src/02-bigOnotation/03-exercises.js diff --git a/src/02-bigOnotation/01-big-o-intro.js b/src/02-bigOnotation/01-big-o-intro.js index a1b821f2..9a740fce 100644 --- a/src/02-bigOnotation/01-big-o-intro.js +++ b/src/02-bigOnotation/01-big-o-intro.js @@ -1,76 +1,72 @@ // Path: src/02-bigOnotation/01-big-o-intro.js // O(1) - Constant Time -function multiplyBy3(num) { - console.log(`${num} * 3 = `, num * 3); +function secondsInDays(numberOfDays) { + if (numberOfDays <= 0 || !Number.isInteger(numberOfDays)) { + throw new Error('Invalid number of days'); + } + return 60 * 60 * 24 * numberOfDays; } console.log('O(1) - Constant Time'); -multiplyBy3(5); -multiplyBy3(50); +console.log('Seconds in 1 day: ', secondsInDays(1)); // 86400 +console.log('Seconds in 10 days: ', secondsInDays(10)); // 864000 +console.log('Seconds in 100 days: ', secondsInDays(100)); // 8640000 // O(n) - Linear Time -function multiplicationTable(num, x) { - for (let i = 1; i <= x; i++) { - console.log(`${num} * ${i} = `, num * i); +function calculateTotalExpenses(monthlyExpenses) { + let total = 0; + for (let i = 0; i < monthlyExpenses.length; i++) { + total += monthlyExpenses[i]; } + return total; } console.log('*******************'); console.log('O(n) - Linear Time'); -console.log('Multiplication table for 5 with x = 3'); -multiplicationTable(5, 3); - -console.log('Multiplication table for 5 with x = 10'); -multiplicationTable(5, 10); +console.log('January: ', calculateTotalExpenses([100, 200, 300])); // 600 +console.log('February: ', calculateTotalExpenses([200, 300, 400])); // 900 +console.log('March: ', calculateTotalExpenses([30, 40, 50, 100, 50])); // 270 // O(n^2) - Quadratic Time -function multiplicationTable2(num, x) { - for (let i = 1; i <= num; i++) { - console.log(`Multiplication table for ${i} with x = ${x}`); - for (let j = 1; j <= x; j++) { - console.log(`${i} * ${j} = `, i * j); +function calculateExpensesMatrix(monthlyExpenses) { + let total = 0; + for (let i = 0; i < monthlyExpenses.length; i++) { + for (let j = 0; j < monthlyExpenses[i].length; j++) { + total += monthlyExpenses[i][j]; } } + return total; } console.log('************************'); console.log('O(n^2) - Quadratic Time'); -multiplicationTable2(3, 2); - -// O(nΛ†3) - Cubic Time -function multiplicationTable3(num, x) { - for (let i = 1; i <= num; i++) { - for (let j = 1; j <= x; j++) { - for (let k = 1; k <= x; k++) { - console.log(`${i} * ${j} * ${k} = `, i * j * k); - } - } - } -} - -console.log('************************'); -console.log('O(n^3) - Cubic Time'); -multiplicationTable3(2, 3); - -// calculating the time complexity of the function -function multiplicationTableRefactored(num, x) { +const monthlyExpenses = [ + [100, 105, 100, 115, 120, 135], + [180, 185, 185, 185, 200, 210], + [30, 30, 30, 30, 30, 30], + [2000, 2000, 2000, 2000, 2000, 2000], + [600, 620, 610, 600, 620, 600], + [150, 100, 130, 200, 150, 100] +]; +console.log('Total expenses: ', calculateExpensesMatrix(monthlyExpenses)); // 18480 - let s = ''; // {1} - let numberOfAsterisks = num * x; // {2} - for (let i = 1; i <= numberOfAsterisks; i++) { // {3} +// calculating the time complexity of the function calculateExpensesMatrix +function multiplicationTable(num, x) { + let s = ''; + let numberOfAsterisks = num * x; + for (let i = 1; i <= numberOfAsterisks; i++) { s += '*'; } - console.log(s); // {4} - console.log('Calculating the time complexity of a function'); // {5} + console.log(s); - for (let i = 1; i <= num; i++) { // {6} - console.log(`Multiplication table for ${i} with x = ${x}`); // {7} - for (let j = 1; j <= x; j++) { // {8} - console.log(`${i} * ${j} = `, i * j); // {9} + for (let i = 1; i <= num; i++) { + console.log(`Multiplication table for ${i} with x = ${x}`); + for (let j = 1; j <= x; j++) { + console.log(`${i} * ${j} = `, i * j); } } } -multiplicationTableRefactored(3, 2); + // to see the output of this file use the command: node src/02-bigOnotation/01-big-o-intro.js \ No newline at end of file diff --git a/src/02-bigOnotation/03-exercises.js b/src/02-bigOnotation/03-exercises.js new file mode 100644 index 00000000..cc09906f --- /dev/null +++ b/src/02-bigOnotation/03-exercises.js @@ -0,0 +1,57 @@ +// Path: src/02-bigOnotation/03-exercises.js + +/* What is the time and space complexities for each of the following functions. + Try them with different inputs to explore how they behave with different inputs */ + +// time complexity: O(1) - Constant Time +// space complexity: O(1) - Constant Space +const oddOrEven = (array) => array.length % 2 === 0 ? 'even' : 'odd'; + +console.log(oddOrEven([1, 2, 3, 4, 5])); // odd +console.log(oddOrEven([1, 2, 3, 4, 5, 6])); // even + +// time complexity: O(n) - Linear Time +// space complexity: O(1) - Constant Space +function calculateAverage(array) { + let sum = 0; + for (let i = 0; i < array.length; i++) { + sum += array[i]; + } + return sum / array.length; +} + +console.log(calculateAverage([1, 2, 3, 4, 5])); // 3 +console.log(calculateAverage([1, 2, 3, 4, 5, 6])); // 3.5 + +// time complexity: O(n^2) - Quadratic Time +// space complexity: O(1) - Constant Space +function hasCommonElements(array1, array2) { + for (let i = 0; i < array1.length; i++) { + for (let j = 0; j < array2.length; j++) { + if (array1[i] === array2[j]) { + return true; + } + } + } + return false; +} + +console.log(hasCommonElements([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])); // false +console.log(hasCommonElements([1, 2, 3, 4, 5], [5, 6, 7, 8, 9])); // true + +// time complexity: O(n) - Linear Time +// space complexity: O(n) - Linear Space +function getOddNumbers(array) { + const result = []; + for (let i = 0; i < array.length; i++) { + if (array[i] % 2 !== 0) { + result.push(array[i]); + } + } + return result; +} + +console.log(getOddNumbers([1, 2, 3, 4, 5])); // [1, 3, 5] +console.log(getOddNumbers([1, 2, 3, 4, 5, 6])); // [1, 3, 5] + +// to see the output of this file use the command: node src/02-bigOnotation/03-exercises.js \ No newline at end of file From b4d028b961cb782813f25dafc97e75a2490ccfc8 Mon Sep 17 00:00:00 2001 From: Loiane Date: Wed, 31 Jul 2024 11:37:19 -0400 Subject: [PATCH 35/43] chapter 9: recursion --- src/09-recursion/01-intro-recursion.js | 23 ++++++++ src/09-recursion/02-factorial.js | 29 ++++++++++ src/09-recursion/03-callstack.js | 17 ++++++ src/09-recursion/04-fibonacci.js | 54 +++++++++++++++++++ src/09-recursion/leetcode/fibonacci-number.ts | 17 ++++++ src/09-recursion/leetcode/power-of-two.ts | 43 +++++++++++++++ 6 files changed, 183 insertions(+) create mode 100644 src/09-recursion/01-intro-recursion.js create mode 100644 src/09-recursion/02-factorial.js create mode 100644 src/09-recursion/03-callstack.js create mode 100644 src/09-recursion/04-fibonacci.js create mode 100644 src/09-recursion/leetcode/fibonacci-number.ts create mode 100644 src/09-recursion/leetcode/power-of-two.ts diff --git a/src/09-recursion/01-intro-recursion.js b/src/09-recursion/01-intro-recursion.js new file mode 100644 index 00000000..0e49c54b --- /dev/null +++ b/src/09-recursion/01-intro-recursion.js @@ -0,0 +1,23 @@ +// src/09-recursion/01-intro-recursion.js + +// To understand recursion, one must first understand recursion +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}); + +function understandRecursion() { + readline.question('Do you understand recursion? (y/n) ', (answer) => { + if (answer.toLowerCase() === 'y') { // Base case + console.log("Excellent! You've grasped recursion."); + readline.close(); // Exit the program + } else { + console.log("Let's try again..."); + understandRecursion(); // Recursive call + } + }); +} + +understandRecursion(); + +// to see the output of this file use the command: node src/09-recursion/01-intro-recursion.js diff --git a/src/09-recursion/02-factorial.js b/src/09-recursion/02-factorial.js new file mode 100644 index 00000000..e42fa188 --- /dev/null +++ b/src/09-recursion/02-factorial.js @@ -0,0 +1,29 @@ +// src/09-recursion/02-factorial.js + +// iterative approach +function factorialIterative(number) { + if (number < 0) { + return undefined; + } + let total = 1; + for (let n = number; n > 1; n--) { + total *= n; + } + return total; +} + +console.log('5! =',factorialIterative(5)); // 5! = 120 + +// recursive approach +function factorial(number) { + // console.trace(); + if (number < 0) { return undefined; } + if (number === 1 || number === 0) { // base case + return 1; + } + return number * factorial(number - 1); +} + +console.log('Recursive 5! =',factorial(5)); // Recursive 5! = 120 + +// to see the output of this file use the command: node src/09-recursion/02-factorial.js \ No newline at end of file diff --git a/src/09-recursion/03-callstack.js b/src/09-recursion/03-callstack.js new file mode 100644 index 00000000..011c1e45 --- /dev/null +++ b/src/09-recursion/03-callstack.js @@ -0,0 +1,17 @@ +// src/09-recursion/03-callstack.js + +// stack overflow +let count = 0; +function recursiveFn() { + count++; + recursiveFn(); +} + +try { + recursiveFn(); +} catch (ex) { + console.log('count = ' + count + ' error: ' + ex); +} + + +// to see the output of this file use the command: node src/09-recursion/03-callstack.js \ No newline at end of file diff --git a/src/09-recursion/04-fibonacci.js b/src/09-recursion/04-fibonacci.js new file mode 100644 index 00000000..abfdf541 --- /dev/null +++ b/src/09-recursion/04-fibonacci.js @@ -0,0 +1,54 @@ +// src/09-recursion/04-fibonacci.js + +// iterative approach +function fibonacciIterative(n) { + if (n < 0) { + throw new Error('Input must be a non-negative integer'); + } + if (n < 2) { return n; } + + let prevPrev = 0; + let prev = 1; + let current; + + for (let i = 2; i <= n; i++) { // n >= 2 + current = prev + prevPrev; // f(n-1) + f(n-2) + prevPrev = prev; + prev = current; + } + + return current; +} + +console.log('fibonacciIterative(2)', fibonacciIterative(2)); // 1 +console.log('fibonacciIterative(3)', fibonacciIterative(3)); // 2 +console.log('fibonacciIterative(4)', fibonacciIterative(4)); // 3 +console.log('fibonacciIterative(5)', fibonacciIterative(5)); // 5 + +// recursive approach +function fibonacci(n) { + if (n < 0) { + throw new Error('Input must be a non-negative integer'); + } + if (n < 2) { return n; } // base case + return fibonacci(n - 1) + fibonacci(n - 2); // recursive case +} + +console.log('fibonacci(5)', fibonacci(5)); // 5 + +// memoization approach +function fibonacciMemoization(n) { + if (n < 0) { + throw new Error('Input must be a non-negative integer'); + } + const memo = [0, 1]; + const fibonacci = (n) => { + if (memo[n] != null) return memo[n]; + return memo[n] = fibonacci(n - 1) + fibonacci(n - 2); + }; + return fibonacci(n); +} + +console.log('fibonacciMemoization(5)', fibonacciMemoization(5)); // 5 + +// to see the output of this file use the command: node src/09-recursion/04-fibonacci.js \ No newline at end of file diff --git a/src/09-recursion/leetcode/fibonacci-number.ts b/src/09-recursion/leetcode/fibonacci-number.ts new file mode 100644 index 00000000..e0130034 --- /dev/null +++ b/src/09-recursion/leetcode/fibonacci-number.ts @@ -0,0 +1,17 @@ +// https://leetcode.com/problems/fibonacci-number/description/ + +function fib(n: number): number { + if (n < 2) { return n; } + + let prevPrev = 0; + let prev = 1; + let current; + + for (let i = 2; i <= n; i++) { // n >= 2 + current = prev + prevPrev; // f(n-1) + f(n-2) + prevPrev = prev; + prev = current; + } + + return current; +}; \ No newline at end of file diff --git a/src/09-recursion/leetcode/power-of-two.ts b/src/09-recursion/leetcode/power-of-two.ts new file mode 100644 index 00000000..bfbbdf22 --- /dev/null +++ b/src/09-recursion/leetcode/power-of-two.ts @@ -0,0 +1,43 @@ +// https://leetcode.com/problems/power-of-two/ + +// function isPowerOfTwo(n: number): boolean { +// if (n <= 0) { +// return false; +// } + +// return (n & (n - 1)) === 0; +// } + +// Time complexity: O(1) +// Space complexity: O(1) + +// Test cases + + +// recursive solution +function isPowerOfTwo(n: number): boolean { + if (n <= 0) { // edge case for negative numbers + return false; + } + if (n % 2 !== 0) { // edge case for odd numbers + return false; + } + + if (n === 1) { // base case + return true; + } + + return isPowerOfTwo(n / 2); +} + +// Time complexity: O(log n) +// Space complexity: O(log n) + +// Test cases +console.log(isPowerOfTwo(1)); // true +console.log(isPowerOfTwo(16)); // true +console.log(isPowerOfTwo(3)); // false +console.log(isPowerOfTwo(4)); // true +console.log(isPowerOfTwo(5)); // false +console.log(isPowerOfTwo(0)); // false +console.log(isPowerOfTwo(-1)); // false From 67c18f576efa41cf4823e167f0ad18660e255b43 Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 8 Aug 2024 11:53:34 -0400 Subject: [PATCH 36/43] chapter 10: trees --- src/10-tree/01-bst.js | 38 +++ src/10-tree/02-in-order-traversal.js | 42 +++ src/10-tree/03-pre-order-traversal.js | 53 ++++ src/10-tree/04-post-order-traversal.js | 34 +++ src/10-tree/05-avl.js | 36 +++ src/10-tree/06-red-black.js | 35 +++ src/10-tree/avl-tree.js | 173 ++++++++++++ src/10-tree/binary-search-tree.js | 171 ++++++++++++ src/10-tree/comparator.js | 33 +++ src/10-tree/compare.js | 7 + src/10-tree/fenwick-tree.js | 46 ++++ .../leetcode/binary-tree-inorder-traversal.ts | 38 +++ .../binary-tree-postorder-traversal.ts | 43 +++ .../binary-tree-preorder-traversal.ts | 37 +++ ...vert-sorted-array-to-binary-search-tree.ts | 22 ++ .../leetcode/maximum-depth-of-binary-tree.ts | 18 ++ .../leetcode/minimum-depth-of-binary-tree.ts | 21 ++ .../leetcode/validate-binary-search-tree.ts | 25 ++ src/10-tree/red-black-tree.js | 250 ++++++++++++++++++ src/10-tree/segment-tree.js | 86 ++++++ 20 files changed, 1208 insertions(+) create mode 100644 src/10-tree/01-bst.js create mode 100644 src/10-tree/02-in-order-traversal.js create mode 100644 src/10-tree/03-pre-order-traversal.js create mode 100644 src/10-tree/04-post-order-traversal.js create mode 100644 src/10-tree/05-avl.js create mode 100644 src/10-tree/06-red-black.js create mode 100644 src/10-tree/avl-tree.js create mode 100644 src/10-tree/binary-search-tree.js create mode 100644 src/10-tree/comparator.js create mode 100644 src/10-tree/compare.js create mode 100644 src/10-tree/fenwick-tree.js create mode 100644 src/10-tree/leetcode/binary-tree-inorder-traversal.ts create mode 100644 src/10-tree/leetcode/binary-tree-postorder-traversal.ts create mode 100644 src/10-tree/leetcode/binary-tree-preorder-traversal.ts create mode 100644 src/10-tree/leetcode/convert-sorted-array-to-binary-search-tree.ts create mode 100644 src/10-tree/leetcode/maximum-depth-of-binary-tree.ts create mode 100644 src/10-tree/leetcode/minimum-depth-of-binary-tree.ts create mode 100644 src/10-tree/leetcode/validate-binary-search-tree.ts create mode 100644 src/10-tree/red-black-tree.js create mode 100644 src/10-tree/segment-tree.js diff --git a/src/10-tree/01-bst.js b/src/10-tree/01-bst.js new file mode 100644 index 00000000..69fe9e12 --- /dev/null +++ b/src/10-tree/01-bst.js @@ -0,0 +1,38 @@ +// src/10-tree/01-bst.js +const BinarySearchTree = require('./binary-search-tree'); + +class Student { + constructor(idNumber, name, gradeLevel, address) { + this.idNumber = idNumber; + this.name = name; + this.gradeLevel = gradeLevel; + } +} + +// create student comparator to compare idNumber +const studentComparator = (a, b) => a.idNumber - b.idNumber; + +const studentTree = new BinarySearchTree(studentComparator); + +studentTree.insert(new Student(11, 'Darcy', 10)); +studentTree.insert(new Student(7, 'Tory', 10)); +studentTree.insert(new Student(5, 'Caleb', 10)); +studentTree.insert(new Student(9, 'Sofia', 10)); +studentTree.insert(new Student(15, 'Max', 10)); + +// 11 +// / \ +// 7 15 +// / \ +// 5 9 + +studentTree.insert(new Student(12, 'Seth', 10)); + +// 11 +// / \ +// 7 15 +// / \ / +// 5 9 12 + + +// to see the output of this file use the command: node src/10-tree/01-bst.js diff --git a/src/10-tree/02-in-order-traversal.js b/src/10-tree/02-in-order-traversal.js new file mode 100644 index 00000000..16fbd015 --- /dev/null +++ b/src/10-tree/02-in-order-traversal.js @@ -0,0 +1,42 @@ +// src/10-tree/02-in-order-traversal.js +const BinarySearchTree = require('./binary-search-tree'); + +class Student { + constructor(idNumber, firstName, lastName) { + this.idNumber = idNumber; + this.lastName = lastName; + this.firstName = firstName; + } +} + +// create student comparator to compare lastName using localCompare +const studentComparator = (a, b) => a.lastName.localeCompare(b.lastName); + +const studentTree = new BinarySearchTree(studentComparator); + +studentTree.insert(new Student(9, 'Sofia', 'Cygnus')); +studentTree.insert(new Student(12, 'Seth', 'Capella')); +studentTree.insert(new Student(11, 'Darcy', 'Vega')); +studentTree.insert(new Student(7, 'Tory', 'Vega')); +studentTree.insert(new Student(5, 'Caleb', 'Altair')); +studentTree.insert(new Student(15, 'Max', 'Rigel')); + + +// in order traversal +const classRoster = []; +const addToRoster = (studentData) => { + classRoster.push(`${studentData.idNumber}: ${studentData.lastName}, ${studentData.firstName}`); +} +studentTree.inOrderTraverse(addToRoster); + +console.log(classRoster); +// [ +// '5: Altair, Caleb', +// '12: Capella, Seth', +// '9: Cygnus, Sofia', +// '15: Rigel, Max', +// '11: Vega, Darcy', +// '7: Vega, Tory' +// ] + +// to see the output of this file use the command: node src/10-tree/02-in-order-traversal.js \ No newline at end of file diff --git a/src/10-tree/03-pre-order-traversal.js b/src/10-tree/03-pre-order-traversal.js new file mode 100644 index 00000000..a84e0689 --- /dev/null +++ b/src/10-tree/03-pre-order-traversal.js @@ -0,0 +1,53 @@ +// src/10-tree/03-pre-order-traversal.js +const BinarySearchTree = require('./binary-search-tree'); + +class Employee { + constructor(id, name, title) { + this.id = id; + this.name = name; + this.title = title; + } +} + +// create title constant +const TITLE = { + CEO: 1, + VP: 2, + MANAGER: 3, + STAFF: 4 +} + +// create employee comparator to compare title +const employeeComparator = (a, b) => a.id - b.id; + +const employeeTree = new BinarySearchTree(employeeComparator); + +employeeTree.insert(new Employee(10, 'Gandalf', TITLE.CEO)); +employeeTree.insert(new Employee(5, 'Frodo', TITLE.VP)); +employeeTree.insert(new Employee(3,'Legolas', TITLE.MANAGER)); +employeeTree.insert(new Employee(1, 'Aragorn', TITLE.STAFF)); +employeeTree.insert(new Employee(4, 'Gimli', TITLE.STAFF)); + +employeeTree.insert(new Employee(14, 'Arya', TITLE.VP)); +employeeTree.insert(new Employee(12, 'John', TITLE.MANAGER)); +employeeTree.insert(new Employee(11, 'Brienne', TITLE.STAFF)); +employeeTree.insert(new Employee(13, 'Tyrion', TITLE.STAFF)); + +// pre order traversal +const sendEmergencyNotification = (employee, message) => { + console.log(`Notifying ${employee.name}: ${message}`); +} +const emergencyMessage = 'Tornado warning in the area. Seek shelter immediately!'; +employeeTree.preOrderTraverse((node) => sendEmergencyNotification(node, emergencyMessage)); + +// Notifying Gandalf: Tornado warning in the area. Seek shelter immediately! +// Notifying Frodo: Tornado warning in the area. Seek shelter immediately! +// Notifying Legolas: Tornado warning in the area. Seek shelter immediately! +// Notifying Arya: Tornado warning in the area. Seek shelter immediately! +// Notifying Aragorn: Tornado warning in the area. Seek shelter immediately! +// Notifying John: Tornado warning in the area. Seek shelter immediately! +// Notifying Gimli: Tornado warning in the area. Seek shelter immediately! +// Notifying Brienne: Tornado warning in the area. Seek shelter immediately! +// Notifying Tyrion: Tornado warning in the area. Seek shelter immediately! + +// to see the output of this file use the command: node src/10-tree/03-pre-order-traversal.js diff --git a/src/10-tree/04-post-order-traversal.js b/src/10-tree/04-post-order-traversal.js new file mode 100644 index 00000000..ef1fdaf6 --- /dev/null +++ b/src/10-tree/04-post-order-traversal.js @@ -0,0 +1,34 @@ +// src/10-tree/04-post-order-traversal.js +const BinarySearchTree = require('./binary-search-tree'); + +class FileOrDirectory { + constructor(name, isDirectory, size = 0) { + this.name = name; + this.isDirectory = isDirectory; // true for directory, false for file + } +} +const fileDirectoryComparator = (a, b) => a.name.localeCompare(b.name); + +const fileSystemTree = new BinarySearchTree(fileDirectoryComparator); +fileSystemTree.insert(new FileOrDirectory('Project', true)); +fileSystemTree.insert(new FileOrDirectory('Documents', true)); +fileSystemTree.insert(new FileOrDirectory('Code', true)); +fileSystemTree.insert(new FileOrDirectory('notes.txt', false)); +fileSystemTree.insert(new FileOrDirectory('design.pdf', false)); +fileSystemTree.insert(new FileOrDirectory('app.js', false)); + + +// post order traversal +const deleteFileOrDirectory = (fileDirectory) => { + console.log(`Deleting ${fileDirectory.name}`); +} +fileSystemTree.postOrderTraverse(deleteFileOrDirectory); + +// Deleting app.js +// Deleting design.pdf +// Deleting Code +// Deleting notes.txt +// Deleting Documents +// Deleting Project + +// to see the output of this file use the command: node src/10-tree/04-post-order-traversal.js \ No newline at end of file diff --git a/src/10-tree/05-avl.js b/src/10-tree/05-avl.js new file mode 100644 index 00000000..731d155f --- /dev/null +++ b/src/10-tree/05-avl.js @@ -0,0 +1,36 @@ +// src/10-tree/05-avl.js + +const AVLTree = require('./avl-tree'); + +class Student { + constructor(idNumber, name, gradeLevel, address) { + this.idNumber = idNumber; + this.name = name; + this.gradeLevel = gradeLevel; + } + toString() { + return `${this.idNumber} - ${this.name}`; + } +} + +// create student comparator to compare idNumber +const studentComparator = (a, b) => a.idNumber - b.idNumber; + +const studentTree = new AVLTree(studentComparator); + +studentTree.insert(new Student(11, 'Darcy', 10)); +studentTree.insert(new Student(7, 'Tory', 10)); +studentTree.insert(new Student(5, 'Caleb', 10)); +studentTree.insert(new Student(9, 'Sofia', 10)); +studentTree.insert(new Student(15, 'Max', 10)); +studentTree.insert(new Student(13, 'Geraldine', 10)); +studentTree.insert(new Student(12, 'Seth', 10)); + + +// 11 +// / \ +// 7 13 +// / \ / \ +// 5 9 12 15 + +// to see the output of this file use the command: node src/10-tree/05-avl.js \ No newline at end of file diff --git a/src/10-tree/06-red-black.js b/src/10-tree/06-red-black.js new file mode 100644 index 00000000..f660c91e --- /dev/null +++ b/src/10-tree/06-red-black.js @@ -0,0 +1,35 @@ +// src/10-tree/06-red-black.js + +const RedBlackTree = require('./red-black-tree'); + +class Student { + constructor(idNumber, name, gradeLevel, address) { + this.idNumber = idNumber; + this.name = name; + this.gradeLevel = gradeLevel; + } + toString() { + return `${this.idNumber} - ${this.name}`; + } +} + +// create student comparator to compare idNumber +const studentComparator = (a, b) => a.idNumber - b.idNumber; + +const studentTree = new RedBlackTree(studentComparator); + +studentTree.insert(new Student(11, 'Darcy', 10)); +studentTree.insert(new Student(7, 'Tory', 10)); +studentTree.insert(new Student(5, 'Caleb', 10)); +studentTree.insert(new Student(9, 'Sofia', 10)); +studentTree.insert(new Student(15, 'Max', 10)); +studentTree.insert(new Student(13, 'Geraldine', 10)); +studentTree.insert(new Student(12, 'Seth', 10)); + +studentTree.print(); + +// console.log('--- Removing 7'); +// studentTree.remove(new Student(7, 'Tory', 10)); +// studentTree.print(); + +// to see the output of this file use the command: node src/10-tree/06-red-black.js \ No newline at end of file diff --git a/src/10-tree/avl-tree.js b/src/10-tree/avl-tree.js new file mode 100644 index 00000000..a0b99884 --- /dev/null +++ b/src/10-tree/avl-tree.js @@ -0,0 +1,173 @@ +// src/10-tree/avl-tree.js +const Comparator = require('./comparator'); +const BinarySearchTree = require('./binary-search-tree'); + +const BalanceFactor = { + UNBALANCED_RIGHT: 1, + SLIGHTLY_UNBALANCED_RIGHT: 2, + BALANCED: 3, + SLIGHTLY_UNBALANCED_LEFT: 4, + UNBALANCED_LEFT: 5 +}; + +class AVLNode { + constructor(data) { + this.data = data; + this.left = null; + this.right = null; + this.height = 1; + } +} + +class AVLTree extends BinarySearchTree { + #root; + #compareFn; + + constructor(compareFn = Comparator.defaultCompareFn) { + super(compareFn); + this.#compareFn = new Comparator(compareFn); + this.#root = null; + } + + insert(data) { + this.#root = this.#insertNode(data, this.#root); + } + + #insertNode(data, currentNode) { + if (!currentNode) { + return new AVLNode(data); + } + + if (this.#compareFn.lessThan(data, currentNode.data)) { + currentNode.left = this.#insertNode(data, currentNode.left); + } else { + currentNode.right = this.#insertNode(data, currentNode.right); + } + + currentNode.height = this.#updateNodeHeight(currentNode); + + return this.#balance(currentNode); + } + + #updateNodeHeight(node) { + return 1 + Math.max(this.#getHeight(node.left), this.#getHeight(node.right)); + } + + #getHeight(node) { + return node ? node.height : 0; + } + + #getBalanceFactor(node) { + const heightDifference = this.#getHeight(node.left) - this.#getHeight(node.right); + switch (heightDifference) { + case -2: return BalanceFactor.UNBALANCED_RIGHT; + case -1: return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT; + case 1: return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT; + case 2: return BalanceFactor.UNBALANCED_LEFT; + default: return BalanceFactor.BALANCED; + } + } + + #balance(node) { + const balanceFactor = this.#getBalanceFactor(node); + if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) { + const leftChildBalanceFactor = this.#getBalanceFactor(node.left); + if (leftChildBalanceFactor === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) { + return this.#rotateRight(node); + } else { + return this.#rotateLeftRight(node); + } + } else if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) { + const rightChildBalanceFactor = this.#getBalanceFactor(node.right); + if (rightChildBalanceFactor === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) { + return this.#rotateLeft(node); + } else { + return this.#rotateRightLeft(node); + } + } + return node; + } + + #rotateRight(node) { + const newRoot = node.left; // Identify the pivot (left child) + const temp = newRoot.right; // Store the right child temporarily + + // Perform the rotation + newRoot.right = node; + node.left = temp; + + // Update heights of the affected nodes + node.height = this.#updateNodeHeight(node); + newRoot.height = this.#updateNodeHeight(newRoot); + + return newRoot; // Return the new root of the subtree + } + + #rotateLeft(node) { + const newRoot = node.right; // Identify the pivot (right child) + const temp = newRoot.left; // Store the left child temporarily + + newRoot.left = node; // Perform the rotation + node.right = temp; + + node.height = this.#updateNodeHeight(node); + newRoot.height = this.#updateNodeHeight(newRoot); + return newRoot; + } + + #rotateLeftRight(node) { + node.left = this.#rotateLeft(node.left); // First, rotate left on the left child + return this.#rotateRight(node); // Then, rotate right on the original node + } + + #rotateRightLeft(node) { + node.right = this.#rotateRight(node.right); // Rotate right on the right child + return this.#rotateLeft(node); // Then, rotate left on the original node + } + + remove(data) { + this.#root = this.#removeNode(data, this.#root); + } + + #removeNode(data, currentNode) { + if (!currentNode) { + return null; + } + + if (this.#compareFn.lessThan(data, currentNode.data)) { + currentNode.left = this.#removeNode(data, currentNode.left); + } else if (this.#compareFn.greaterThan(data, currentNode.data)) { + currentNode.right = this.#removeNode(data, currentNode.right); + } else { + if (!currentNode.left && !currentNode.right) { + return null; + } + + if (!currentNode.left) { + return currentNode.right; + } + + if (!currentNode.right) { + return currentNode.left; + } + + const minNode = this.#findMinNode(currentNode.right); + currentNode.data = minNode.data; + currentNode.right = this.#removeNode(minNode.data, currentNode.right); + } + + currentNode.height = this.#updateNodeHeight(currentNode); + + return this.#balance(currentNode); + } + + #findMinNode(node) { + if (!node.left) { + return node; + } + return this.#findMinNode(node.left); + } + +} + +module.exports = AVLTree; \ No newline at end of file diff --git a/src/10-tree/binary-search-tree.js b/src/10-tree/binary-search-tree.js new file mode 100644 index 00000000..598b73e7 --- /dev/null +++ b/src/10-tree/binary-search-tree.js @@ -0,0 +1,171 @@ +// src/10-tree/binary-search-tree.js + +const Comparator = require('./comparator'); + +class BSTNode { + constructor(data) { + this.data = data; + this.left = null; + this.right = null; + } +} + +class BinarySearchTree { + #root; + #compareFn; + + constructor(compareFn = Comparator.defaultCompareFn) { + this.#compareFn = new Comparator(compareFn); + this.#root = null; + } + + insert(data) { + if (!this.#root) { + this.#root = new BSTNode(data); + } else { + this.#insertNode(data, this.#root); + } + } + + #insertNode(data, currentNode) { + if (this.#compareFn.lessThan(data, currentNode.data)) { + if (!currentNode.left) { + currentNode.left = new BSTNode(data); + } else { + this.#insertNode(data, currentNode.left); + } + } else { + if (!currentNode.right) { + currentNode.right = new BSTNode(data); + } else { + this.#insertNode(data, currentNode.right); + } + } + } + + search(data) { + return this.#searchNode(data, this.#root); + } + + #searchNode(data, currentNode) { + if (!currentNode) { + return false; + } + + if (this.#compareFn.equal(data, currentNode.data)) { + return true; + } + + if (this.#compareFn.lessThan(data, currentNode.data)) { + return this.#searchNode(data, currentNode.left); + } else { + return this.#searchNode(data, currentNode.right); + } + } + + remove(data) { + this.#root = this.#removeNode(data, this.#root); + } + + #removeNode(data, currentNode) { + if (!currentNode) { + return null; + } + + if (this.#compareFn.lessThan(data, currentNode.data)) { + currentNode.left = this.#removeNode(data, currentNode.left); + return currentNode; + } else if (this.#compareFn.greaterThan(data, currentNode.data)) { + currentNode.right = this.#removeNode(data, currentNode.right); + return currentNode; + } else { + if (!currentNode.left && !currentNode.right) { + return null; + } + + if (!currentNode.left) { + return currentNode.right; + } + + if (!currentNode.right) { + return currentNode.left; + } + + const minNode = this.#findMinNode(currentNode.right); + currentNode.data = minNode.data; + currentNode.right = this.#removeNode(minNode.data, currentNode.right); + return currentNode; + } + } + + min() { + if (!this.#root) { + return null; + } + return this.#findMinNode(this.#root).data; + } + + #findMinNode(node) { + if (!node.left) { + return node; + } + return this.#findMinNode(node.left); + } + + max() { + if (!this.#root) { + return null; + } + return this.#findMaxNode(this.#root).data; + } + + #findMaxNode(node) { + if (!node.right) { + return node; + } + return this.#findMaxNode(node.right); + } + + get root() { + return this.#root; + } + + inOrderTraverse(callback) { + this.#inOrderTraverseNode(this.#root, callback); + } + + #inOrderTraverseNode(node, callback) { + if (node) { + this.#inOrderTraverseNode(node.left, callback); + callback(node.data); + this.#inOrderTraverseNode(node.right, callback); + } + } + + preOrderTraverse(callback) { + this.#preOrderTraverseNode(this.#root, callback); + } + + #preOrderTraverseNode(node, callback) { + if (node) { + callback(node.data); + this.#preOrderTraverseNode(node.left, callback); + this.#preOrderTraverseNode(node.right, callback); + } + } + + postOrderTraverse(callback) { + this.#postOrderTraverseNode(this.#root, callback); + } + + #postOrderTraverseNode(node, callback) { + if (node) { + this.#postOrderTraverseNode(node.left, callback); + this.#postOrderTraverseNode(node.right, callback); + callback(node.data); + } + } + +} + +module.exports = BinarySearchTree; \ No newline at end of file diff --git a/src/10-tree/comparator.js b/src/10-tree/comparator.js new file mode 100644 index 00000000..5f718e7b --- /dev/null +++ b/src/10-tree/comparator.js @@ -0,0 +1,33 @@ +// src/10-tree/comparator.js + +const Compare = require('./compare'); + +class Comparator { + #compareFn; + + constructor(compareFn = Comparator.defaultCompareFn) { + this.#compareFn = compareFn; + } + + static defaultCompareFn(a, b) { + if (a === b) { + return Compare.EQUALS; + } + return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; + } + + equal(a, b) { + return this.#compareFn(a, b) === Compare.EQUALS; + } + + lessThan(a, b) { + return this.#compareFn(a, b) < Compare.EQUALS; + } + + greaterThan(a, b) { + return this.#compareFn(a, b) > Compare.EQUALS; + } + +} + +module.exports = Comparator; \ No newline at end of file diff --git a/src/10-tree/compare.js b/src/10-tree/compare.js new file mode 100644 index 00000000..9a23bee0 --- /dev/null +++ b/src/10-tree/compare.js @@ -0,0 +1,7 @@ +const Compare = { + LESS_THAN: -1, + BIGGER_THAN: 1, + EQUALS: 0 +}; + +module.exports = Compare; \ No newline at end of file diff --git a/src/10-tree/fenwick-tree.js b/src/10-tree/fenwick-tree.js new file mode 100644 index 00000000..342df8db --- /dev/null +++ b/src/10-tree/fenwick-tree.js @@ -0,0 +1,46 @@ +// Binary Indexed Tree (Fenwick Tree) +class FenwickTree { + #arraySize; + #tree; + + constructor(arraySize) { + this.#arraySize = arraySize; + this.#tree = Array(arraySize + 1).fill(0); + } + + update(index, value) { + if (index < 1 || index > this.#arraySize) { + throw new Error('Index is out of range'); + } + + for (let i = index; i <= this.#arraySize; i += this.#lowBit(i)) { + this.#tree[i] += value; + } + } + + query(index) { + if (index < 1 || index > this.#arraySize) { + throw new Error('Index is out of range'); + } + + let sum = 0; + + for (let i = index; i > 0; i -= this.#lowBit(i)) { + sum += this.#tree[i]; + } + + return sum; + } + + #lowBit(x) { + return x & -x; + } + + get arraySize() { + return this.#arraySize; + } + + toString() { + return this.#tree.join(', '); + } +} \ No newline at end of file diff --git a/src/10-tree/leetcode/binary-tree-inorder-traversal.ts b/src/10-tree/leetcode/binary-tree-inorder-traversal.ts new file mode 100644 index 00000000..896b547d --- /dev/null +++ b/src/10-tree/leetcode/binary-tree-inorder-traversal.ts @@ -0,0 +1,38 @@ +// https://leetcode.com/problems/binary-tree-inorder-traversal/ +// 94. Binary Tree Inorder Traversal + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function inorderTraversal(root: TreeNode | null): number[] { + // Recursive + //if (!root) return []; + //return [...inorderTraversal(root.left), root.val, ...inorderTraversal(root.right)]; + + // Iterative + const stack: TreeNode[] = []; + const result: number[] = []; + let current = root; + while (stack.length !== 0 || current !== null) { + if (current) { + stack.push(current) + current = current.left; + } else { + current = stack.pop(); + result.push(current.val); + current = current.right; + } + } + return result; +}; + +// Time complexity: O(n) +// Space complexity: O(n) (worst case) - O(log n) (average case) diff --git a/src/10-tree/leetcode/binary-tree-postorder-traversal.ts b/src/10-tree/leetcode/binary-tree-postorder-traversal.ts new file mode 100644 index 00000000..549af7fa --- /dev/null +++ b/src/10-tree/leetcode/binary-tree-postorder-traversal.ts @@ -0,0 +1,43 @@ +// https://leetcode.com/problems/binary-tree-postorder-traversal/description/ +// 145. Binary Tree Postorder Traversal + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function postorderTraversal(root: TreeNode | null): number[] { + // Recursive + //if (!root) return []; + //return [...postorderTraversal(root.left), ...postorderTraversal(root.right), root.val]; + + // Iterative + const stack: TreeNode[] = []; + const result: number[] = []; + let current = root; + let lastVisited: TreeNode | null = null; + while (stack.length !== 0 || current !== null) { + if (current) { + stack.push(current) + current = current.left; + } else { + const peek = stack[stack.length - 1]; + if (peek.right && peek.right !== lastVisited) { + current = peek.right; + } else { + result.push(peek.val); + lastVisited = stack.pop(); + } + } + } + return result; +}; + +// Time complexity: O(n) +// Space complexity: O(n) (worst case) - O(log n) (average case) \ No newline at end of file diff --git a/src/10-tree/leetcode/binary-tree-preorder-traversal.ts b/src/10-tree/leetcode/binary-tree-preorder-traversal.ts new file mode 100644 index 00000000..fd403226 --- /dev/null +++ b/src/10-tree/leetcode/binary-tree-preorder-traversal.ts @@ -0,0 +1,37 @@ +// https://leetcode.com/problems/binary-tree-preorder-traversal/ +// 144. Binary Tree Preorder Traversal + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function preorderTraversal(root: TreeNode | null): number[] { + // Recursive + //if (!root) return []; + //return [root.val, ...preorderTraversal(root.left), ...preorderTraversal(root.right)]; + + // Iterative + const stack: TreeNode[] = []; + const result: number[] = []; + let current = root; + while (stack.length !== 0 || current !== null) { + if (current) { + result.push(current.val); + if (current.right) stack.push(current.right); + current = current.left; + } else { + current = stack.pop(); + } + } + return result; +}; + +// Time complexity: O(n) +// Space complexity: O(n) (worst case) - O(log n) (average case) \ No newline at end of file diff --git a/src/10-tree/leetcode/convert-sorted-array-to-binary-search-tree.ts b/src/10-tree/leetcode/convert-sorted-array-to-binary-search-tree.ts new file mode 100644 index 00000000..26d08878 --- /dev/null +++ b/src/10-tree/leetcode/convert-sorted-array-to-binary-search-tree.ts @@ -0,0 +1,22 @@ +// https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/ +// 108. Convert Sorted Array to Binary Search Tree + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function sortedArrayToBST(nums: number[]): TreeNode | null { + if (nums.length === 0) return null; + const mid = Math.floor(nums.length / 2); + const root = new TreeNode(nums[mid]); + root.left = sortedArrayToBST(nums.slice(0, mid)); + root.right = sortedArrayToBST(nums.slice(mid + 1)); + return root; +} \ No newline at end of file diff --git a/src/10-tree/leetcode/maximum-depth-of-binary-tree.ts b/src/10-tree/leetcode/maximum-depth-of-binary-tree.ts new file mode 100644 index 00000000..836f6d85 --- /dev/null +++ b/src/10-tree/leetcode/maximum-depth-of-binary-tree.ts @@ -0,0 +1,18 @@ +// https://leetcode.com/problems/maximum-depth-of-binary-tree/description/ +// 104. Maximum Depth of Binary Tree + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); +} \ No newline at end of file diff --git a/src/10-tree/leetcode/minimum-depth-of-binary-tree.ts b/src/10-tree/leetcode/minimum-depth-of-binary-tree.ts new file mode 100644 index 00000000..6c49738f --- /dev/null +++ b/src/10-tree/leetcode/minimum-depth-of-binary-tree.ts @@ -0,0 +1,21 @@ +// https://leetcode.com/problems/minimum-depth-of-binary-tree/description/ +// 111. Minimum Depth of Binary Tree + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function minDepth(root: TreeNode | null): number { + if (!root) return 0; + if (!root.left && !root.right) return 1; + if (!root.left) return 1 + minDepth(root.right); + if (!root.right) return 1 + minDepth(root.left); + return 1 + Math.min(minDepth(root.left), minDepth(root.right)); +} \ No newline at end of file diff --git a/src/10-tree/leetcode/validate-binary-search-tree.ts b/src/10-tree/leetcode/validate-binary-search-tree.ts new file mode 100644 index 00000000..ea38c6a5 --- /dev/null +++ b/src/10-tree/leetcode/validate-binary-search-tree.ts @@ -0,0 +1,25 @@ +// https://leetcode.com/problems/validate-binary-search-tree/description/ +// 98. Validate Binary Search Tree + +class TreeNode { + val: number + left: TreeNode | null + right: TreeNode | null + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) + } +} + +function isValidBST(root: TreeNode | null): boolean { + return isValid(root, null, null); +} + +function isValid(node: TreeNode | null, min: number | null, max: number | null): boolean { + if (!node) return true; + if (min !== null && node.val <= min) return false; + if (max !== null && node.val >= max) return false; + return isValid(node.left, min, node.val) && isValid(node.right, node.val, max); +} + diff --git a/src/10-tree/red-black-tree.js b/src/10-tree/red-black-tree.js new file mode 100644 index 00000000..2b207d77 --- /dev/null +++ b/src/10-tree/red-black-tree.js @@ -0,0 +1,250 @@ +// src/10-tree/red-black-tree.js + +const Comparator = require('./comparator'); +const BinarySearchTree = require('./binary-search-tree'); + +const NodeColor = { + RED: 0, + BLACK: 1, +}; + +class RedBlackNode { + constructor(data) { + this.data = data; + this.color = NodeColor.RED; + this.left = null; + this.right = null; + this.parent = null; + } + isRed() { + return this.color === NodeColor.RED; + } +} + +class RedBlackTree extends BinarySearchTree { + #root; + #compareFn; + + constructor(compareFn = Comparator.defaultCompareFn) { + super(compareFn); + this.#compareFn = new Comparator(compareFn); + this.#root = null; + } + + + // Insert a node + insert(data) { + if (this.#root) { + const newNode = this.#insertNode(this.#root, data); + this.#fixTreeProperties(newNode); + } else { + this.#root = new RedBlackNode(data); + this.#root.color = NodeColor.BLACK; + } + } + + #insertNode(node, data) { + if (this.#compareFn.lessThan(data, node.data)) { + if (!node.left) { + node.left = new RedBlackNode(data); + node.left.parent = node; + return node.left; + } else { + return this.#insertNode(node.left, data); + } + } else { + if (!node.right) { + node.right = new RedBlackNode(data); + node.right.parent = node; + return node.right; + } else { + return this.#insertNode(node.right, data); + } + } + } + + #fixTreeProperties(node) { + while (node && node !== this.#root && node.isRed() && node.parent.isRed()) { + let parent = node.parent; + let grandParent = parent.parent; + + // case A: parent is left child of grand parent + if (parent === grandParent.left) { + const uncle = grandParent.right; + // case 1: uncle is red - only recoloring + if (uncle && uncle.isRed()) { + this.#flipColors(grandParent, parent, uncle); + node = grandParent; + } else { + // case 2: node is right child of parent - left rotate + if (node === parent.right) { + this.#rotateLeft(parent); + node = parent; + parent = node.parent; + } + // case 3: node is left child of parent - right rotate + this.#rotateRight(grandParent); + [parent.color, grandParent.color] = [grandParent.color, parent.color]; + node = parent; + } + } else { // case B: parent is right child of grand parent + const uncle = grandParent.left; + + // case 1: uncle is red - only recoloring + if (uncle && uncle.isRed()) { + this.#flipColors(grandParent, parent, uncle); + node = grandParent; + } else { + // case 2: node is left child of parent + if (node === parent.left) { + this.#rotateRight(parent); + node = parent; + parent = node.parent; + } + + // case 3: node is right child of parent + this.#rotateLeft(grandParent); + [parent.color, grandParent.color] = [grandParent.color, parent.color]; + node = parent; + } + } + } + this.#root.color = NodeColor.BLACK; // root should always be black + } + + #flipColors(grandParent, parent, uncle) { + grandParent.color = NodeColor.RED; + parent.color = NodeColor.BLACK; + uncle.color = NodeColor.BLACK; + } + + #rotateLeft(node) { + const newRoot = node.right; + node.right = newRoot.left; + + if (newRoot.left) { + newRoot.left.parent = node; + } + + newRoot.parent = node.parent; + + if (!node.parent) { + this.#root = newRoot; + } else if (node === node.parent.left) { + node.parent.left = newRoot; + } else { + node.parent.right = newRoot; + } + + newRoot.left = node; + node.parent = newRoot; + } + + #rotateRight(node) { + const newRoot = node.left; + node.left = newRoot.right; + + if (newRoot.right) { + newRoot.right.parent = node; + } + + newRoot.parent = node.parent; + + if (!node.parent) { + this.#root = newRoot; + } else if (node === node.parent.right) { + node.parent.right = newRoot; + } else { + node.parent.left = newRoot; + } + + newRoot.right = node; + node.parent = newRoot; + } + + remove(data) { + this.#root = this.#removeNode(data, this.#root); + } + + #removeNode(data, currentNode) { + if (!currentNode) { + return null; + } + + if (this.#compareFn.lessThan(data, currentNode.data)) { + currentNode.left = this.#removeNode(data, currentNode.left); + } else if (this.#compareFn.greaterThan(data, currentNode.data)) { + currentNode.right = this.#removeNode(data, currentNode.right); + } else { + if (!currentNode.left && !currentNode.right) { + return null; + } + + if (!currentNode.left) { + return currentNode.right; + } + + if (!currentNode.right) { + return currentNode.left; + } + + const minNode = this.#findMinNode(currentNode.right); + currentNode.data = minNode.data; + currentNode.right = this.#removeNode(minNode.data, currentNode.right); + } + + return this.#balance(currentNode); + } + + #findMinNode(node) { + if (!node.left) { + return node; + } + return this.#findMinNode(node.left); + } + + #balance(node) { + if (node.isRed()) { + return node; + } + + if (node.left && node.left.isRed()) { + if (node.left.left && node.left.left.isRed()) { + return this.#rotateRight(node); + } + + if (node.left.right && node.left.right.isRed()) { + node.left = this.#rotateLeft(node.left); + return this.#rotateRight(node); + } + } + + if (node.right && node.right.isRed()) { + if (node.right.right && node.right.right.isRed()) { + return this.#rotateLeft(node); + } + + if (node.right.left && node.right.left.isRed()) { + node.right = this.#rotateRight(node.right); + return this.#rotateLeft(node); + } + } + + return node; + } + + printInorder(node) { + if (node === null) { + return; + } + this.printInorder(node.left); + console.log(node.data, node.color); + this.printInorder(node.right); + } + + print() { + this.printInorder(this.#root); + } +} + +module.exports = RedBlackTree; \ No newline at end of file diff --git a/src/10-tree/segment-tree.js b/src/10-tree/segment-tree.js new file mode 100644 index 00000000..3bcc929a --- /dev/null +++ b/src/10-tree/segment-tree.js @@ -0,0 +1,86 @@ + +class SegmentTree { + #inputArray; + #segmentTree; + #operationFallback; + + constructor(inputArray, operationFallback) { + this.#inputArray = inputArray; + this.#operationFallback = operationFallback; + this.#segmentTree = Array(inputArray.length * 4).fill(0); + + this.#build(0, 0, inputArray.length - 1); + } + + #build(currentIndex, leftIndex, rightIndex) { + if (leftIndex === rightIndex) { + this.#segmentTree[currentIndex] = this.#inputArray[leftIndex]; + return; + } + + const middleIndex = Math.floor((leftIndex + rightIndex) / 2); + const leftChildIndex = 2 * currentIndex + 1; + const rightChildIndex = 2 * currentIndex + 2; + + this.#build(leftChildIndex, leftIndex, middleIndex); + this.#build(rightChildIndex, middleIndex + 1, rightIndex); + + this.#segmentTree[currentIndex] = this.#operationFallback( + this.#segmentTree[leftChildIndex], + this.#segmentTree[rightChildIndex] + ); + } + + query(leftIndex, rightIndex) { + return this.#query(0, 0, this.#inputArray.length - 1, leftIndex, rightIndex); + } + + #query(currentIndex, leftIndex, rightIndex, queryLeftIndex, queryRightIndex) { + if (queryLeftIndex > rightIndex || queryRightIndex < leftIndex) { + return 0; + } + + if (queryLeftIndex <= leftIndex && queryRightIndex >= rightIndex) { + return this.#segmentTree[currentIndex]; + } + + const middleIndex = Math.floor((leftIndex + rightIndex) / 2); + const leftChildIndex = 2 * currentIndex + 1; + const rightChildIndex = 2 * currentIndex + 2; + + return this.#operationFallback( + this.#query(leftChildIndex, leftIndex, middleIndex, queryLeftIndex, queryRightIndex), + this.#query(rightChildIndex, middleIndex + 1, rightIndex, queryLeftIndex, queryRightIndex) + ); + } + + update(index, value) { + this.#update(0, 0, this.#inputArray.length - 1, index, value); + } + + #update(currentIndex, leftIndex, rightIndex, index, value) { + if (leftIndex === rightIndex) { + this.#segmentTree[currentIndex] = value; + return; + } + + const middleIndex = Math.floor((leftIndex + rightIndex) / 2); + const leftChildIndex = 2 * currentIndex + 1; + const rightChildIndex = 2 * currentIndex + 2; + + if (index <= middleIndex) { + this.#update(leftChildIndex, leftIndex, middleIndex, index, value); + } else { + this.#update(rightChildIndex, middleIndex + 1, rightIndex, index, value); + } + + this.#segmentTree[currentIndex] = this.#operationFallback( + this.#segmentTree[leftChildIndex], + this.#segmentTree[rightChildIndex] + ); + } + + toString() { + return this.#segmentTree.join(', '); + } +} \ No newline at end of file From 10000ce419fca9609877c076df2e064638d12dab Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 8 Aug 2024 20:34:13 -0400 Subject: [PATCH 37/43] chapter 11: heap --- src/11-heap/01-min-heap.js | 43 +++++++ src/11-heap/02-max-heap.js | 34 ++++++ src/11-heap/03-heap-sort.js | 29 +++++ src/11-heap/heap-sort.js | 57 ++++++++++ src/11-heap/heap.js | 120 ++++++++++++++++++++ src/11-heap/leetcode/minimum-number-game.ts | 58 ++++++++++ src/11-heap/leetcode/relative-ranks.ts | 90 +++++++++++++++ 7 files changed, 431 insertions(+) create mode 100644 src/11-heap/01-min-heap.js create mode 100644 src/11-heap/02-max-heap.js create mode 100644 src/11-heap/03-heap-sort.js create mode 100644 src/11-heap/heap-sort.js create mode 100644 src/11-heap/heap.js create mode 100644 src/11-heap/leetcode/minimum-number-game.ts create mode 100644 src/11-heap/leetcode/relative-ranks.ts diff --git a/src/11-heap/01-min-heap.js b/src/11-heap/01-min-heap.js new file mode 100644 index 00000000..61d03ab8 --- /dev/null +++ b/src/11-heap/01-min-heap.js @@ -0,0 +1,43 @@ +// src/11-heap/01-min-heap.js + +const Heap = require('./heap'); + +class Task { + constructor(id, priority, executionTime) { + this.id = id; + this.priority = priority; + this.executionTime = executionTime; + } +} + +class TaskScheduler { + constructor() { + this.taskQueue = new Heap((a,b) => a.priority - b.priority); + } + + addTask(task) { + this.taskQueue.insert(task); + } + + scheduleNextTask() { + if (this.taskQueue.isEmpty()) { + return null; // No tasks to schedule + } + return this.taskQueue.extract(); + } +} + +const scheduler = new TaskScheduler(); +scheduler.addTask(new Task(2, 2, 10)); +scheduler.addTask(new Task(3, 3, 5)); +scheduler.addTask(new Task(4, 4, 8)); +scheduler.addTask(new Task(5, 5, 15)); +scheduler.addTask(new Task(1, 1, 20)); + +console.log(scheduler.taskQueue.toArray()); + +console.log(scheduler.scheduleNextTask()); // Output: Task 1 (highest priority) + +console.log(scheduler.taskQueue.toArray()); + +// to see the output of this file use the command: node src/11-heap/01-min-heap.js diff --git a/src/11-heap/02-max-heap.js b/src/11-heap/02-max-heap.js new file mode 100644 index 00000000..ac1adde1 --- /dev/null +++ b/src/11-heap/02-max-heap.js @@ -0,0 +1,34 @@ +// src/11-heap/02-max-heap.js + +const Heap = require('./heap'); + +class Patient { + constructor(name, priority) { + this.name = name; + this.priority = priority; + } +} + +const PRIORITY = { LOW: 1, MEDIUM: 2, HIGH: 3 }; + +const erHeap = new Heap((a, b) => b.priority - a.priority); + +erHeap.insert(new Patient('Poppy', PRIORITY.LOW)); +erHeap.insert(new Patient('Kieran', PRIORITY.HIGH)); +erHeap.insert(new Patient('Camila', PRIORITY.MEDIUM)); +erHeap.insert(new Patient('Casteel', PRIORITY.LOW)); +erHeap.insert(new Patient('Mike', PRIORITY.HIGH)); + +console.log('Head', erHeap.toArray()); + +while (!erHeap.isEmpty()) { + const patient = erHeap.extract(); + console.log('Next patient:', patient.name, patient.priority); +} +// Next patient: Kieran 3 +// Next patient: Mike 3 +// Next patient: Camila 2 +// Next patient: Casteel 1 +// Next patient: Poppy 1 + +// to see the output of this file use the command: node src/11-heap/02-max-heap.js diff --git a/src/11-heap/03-heap-sort.js b/src/11-heap/03-heap-sort.js new file mode 100644 index 00000000..d6be093b --- /dev/null +++ b/src/11-heap/03-heap-sort.js @@ -0,0 +1,29 @@ +// src/11-heap/03-heap-sort.js + +const Comparator = require('../10-tree/comparator'); +const Heap = require('./heap'); +const HeapSort = require('./heap-sort'); + +const heapSort = (array, compareFn = Comparator.defaultCompareFn) => { + const heap = new Heap(compareFn); + heap.heapify(array); + + const sortedArray = []; + while (!heap.isEmpty()){ + sortedArray.push(heap.extract()); + } + + + return heap.toArray(); +}; + +// using the Heap class and the heapify method +let unsortedArray = [7, 6, 3, 5, 4, 1, 2]; +console.log(heapSort(unsortedArray)); // [1, 2, 3, 4, 5, 6, 7] + +// using the heap sort algorithm +const unsortedArray = [7, 6, 3, 5, 4, 1, 2]; +HeapSort(unsortedArray); // will modify the array in place +console.log(unsortedArray); // [1, 2, 3, 4, 5, 6, 7] + +// to see the output of this file use the command: node src/11-heap/03-heap-sort.js \ No newline at end of file diff --git a/src/11-heap/heap-sort.js b/src/11-heap/heap-sort.js new file mode 100644 index 00000000..ebd1509a --- /dev/null +++ b/src/11-heap/heap-sort.js @@ -0,0 +1,57 @@ +// src/11-heap/heap-sort.js + +const Comparator = require('../10-tree/comparator'); + +/** + * Heapify the array. + * @param {*} array to be heapified. + * @param {*} heapSize size of the heap. + * @param {*} rootIndex index of the root. + * @param {*} compareFn compare function. +*/ +const siftDown = (array, heapSize, rootIndex, compareFn) => { + let largest = rootIndex; + let left = 2 * rootIndex + 1; + let right = 2 * rootIndex + 2; + + if (left < heapSize && compareFn.greaterThan(array[left], array[largest])) { + largest = left; + } + + if (right < heapSize && compareFn.greaterThan(array[right], array[largest])) { + largest = right; + } + + if (largest !== rootIndex) { + [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; + siftDown(array, heapSize, largest, compareFn); + } +}; + +/** + * Build max heap. + * @param {*} array to be heapified. + * @param {*} compareFn compare function. + */ +const buildMaxHeap = (array, compareFn) => { + const heapSize = array.length; + for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { + siftDown(array, heapSize, i, compareFn); + } +} + +/** + * Heap sort algorithm. + * @param {*} array to be sorted. + * @param {*} compareFn compare function. + */ +const HeapSort = (array, compareFn = Comparator.defaultCompareFn) => { + const defaultCompareFn = new Comparator(compareFn); + buildMaxHeap(array, defaultCompareFn); + for (let i = array.length - 1; i > 0; i--) { + [array[0], array[i]] = [array[i], array[0]]; + siftDown(array, i, 0, defaultCompareFn); + } +} + +module.exports = HeapSort; \ No newline at end of file diff --git a/src/11-heap/heap.js b/src/11-heap/heap.js new file mode 100644 index 00000000..85d210fc --- /dev/null +++ b/src/11-heap/heap.js @@ -0,0 +1,120 @@ +// src/11-heap/heap.js + +const Comparator = require('../10-tree/comparator'); + +class Heap { + #heap = []; + #compareFn; + + constructor(compareFn = Comparator.defaultCompareFn) { + this.#compareFn = new Comparator(compareFn); + } + + getLeftChildIndex(parentIndex) { + return 2 * parentIndex + 1; + } + + getRightChildIndex(parentIndex) { + return 2 * parentIndex + 2; + } + + getParentIndex(childIndex) { + if (childIndex === 0) { return undefined; } + return Math.floor((childIndex - 1) / 2); + } + + insert(value) { + if (value) { + const index = this.#heap.length; + this.#heap.push(value); + this.#siftUp(index); + return true; + } + return false; + } + + #siftUp(index) { + const parentIndex = this.getParentIndex(index); + + if (parentIndex !== undefined && parentIndex >= 0 && + this.#compareFn.greaterThan(this.#heap[parentIndex], this.#heap[index]) + ) { + [this.#heap[parentIndex], this.#heap[index]] = [this.#heap[index], this.#heap[parentIndex]]; + this.#siftUp(parentIndex); + } + } + + extract() { + if (this.#heap.length === 0) { + return undefined; + } + + if (this.#heap.length === 1) { + return this.#heap.shift(); + } + + const root = this.#heap[0]; + this.#heap[0] = this.#heap.pop(); + this.#siftDown(0); + return root; + } + + #siftDown(index) { + const leftIndex = this.getLeftChildIndex(index); + const rightIndex = this.getRightChildIndex(index); + let smallerIndex = index; + + if (leftIndex < this.#heap.length && + this.#compareFn.lessThan(this.#heap[leftIndex], this.#heap[smallerIndex]) + ) { + smallerIndex = leftIndex; + } + + if (rightIndex < this.#heap.length && + this.#compareFn.lessThan(this.#heap[rightIndex], this.#heap[smallerIndex]) + ) { + smallerIndex = rightIndex; + } + + if (smallerIndex !== index) { + [this.#heap[index], this.#heap[smallerIndex]] = [this.#heap[smallerIndex], this.#heap[index]]; + this.#siftDown(smallerIndex); + } + } + + heapify(array) { + this.#heap = array; + const lastParentIndex = this.getParentIndex(this.#heap.length - 1); + if (lastParentIndex !== undefined) { + for (let i = lastParentIndex; i >= 0; i--) { + this.#siftDown(i); + } + } + } + + peek() { + return this.#heap.length === 0 ? undefined : this.#heap[0]; + } + + get size() { + return this.#heap.length; + } + + isEmpty() { + return this.#heap.length === 0; + } + + toArray() { + return this.#heap.slice(); + } + + clear() { + this.#heap = []; + } + + toString() { + return this.#heap.toString(); + } +} + +module.exports = Heap; \ No newline at end of file diff --git a/src/11-heap/leetcode/minimum-number-game.ts b/src/11-heap/leetcode/minimum-number-game.ts new file mode 100644 index 00000000..1e1ecc13 --- /dev/null +++ b/src/11-heap/leetcode/minimum-number-game.ts @@ -0,0 +1,58 @@ +// https://leetcode.com/problems/minimum-number-game/description/ +// 2974. Minimum Number Game + +function siftDown (array: number[], heapSize: number, rootIndex: number) { + let largest = rootIndex; + let left = 2 * rootIndex + 1; + let right = 2 * rootIndex + 2; + + if (left < heapSize && array[left] > array[largest]) { + largest = left; + } + + if (right < heapSize && array[right] > array[largest]) { + largest = right; + } + + if (largest !== rootIndex) { + [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; + siftDown(array, heapSize, largest); + } +}; + + +function buildMaxHeap (array: number[]) { + const heapSize = array.length; + for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { + siftDown(array, heapSize, i); + } +} + +function heapSort(array: number[]) { + buildMaxHeap(array); + for (let i = array.length - 1; i > 0; i--) { + [array[0], array[i]] = [array[i], array[0]]; + siftDown(array, i, 0); + } +} + +function numberGame(nums: number[]): number[] { + // nums.sort((a, b) => a - b); + heapSort(nums); + const arr: number[] = []; + while (nums.length > 0) { + const alice = nums.splice(0, 1)[0]; + const bob = nums.splice(0, 1)[0]; + arr.push(bob); + arr.push(alice); + } + return arr; +}; + +console.log(numberGame([5,4,2,3])); // [ 3, 2, 5, 4 ] +console.log(numberGame([2,5])); // [ 5, 2 ] + +// Time complexity: O(nlogn) +// Space complexity: O(n) + +// to see the output of this file use the command: npx ts-node src/11-heap/leetcode/minimum-number-game.ts \ No newline at end of file diff --git a/src/11-heap/leetcode/relative-ranks.ts b/src/11-heap/leetcode/relative-ranks.ts new file mode 100644 index 00000000..af05e336 --- /dev/null +++ b/src/11-heap/leetcode/relative-ranks.ts @@ -0,0 +1,90 @@ +// https://leetcode.com/problems/relative-ranks/description/ +// 506. Relative Ranks + +function findRelativeRanks(nums: number[]): string[] { + const sorted = [...nums].sort((a, b) => b - a); + const map = new Map(); + for (let i = 0; i < sorted.length; i++) { + if (i === 0) { + map.set(sorted[i], 'Gold Medal'); + } else if (i === 1) { + map.set(sorted[i], 'Silver Medal'); + } else if (i === 2) { + map.set(sorted[i], 'Bronze Medal'); + } else { + map.set(sorted[i], (i + 1).toString()); + } + } + + return nums.map((num) => map.get(num)!); +} + +// console.log(findRelativeRanks([5, 4, 3, 2, 1])); // ["Gold Medal","Silver Medal","Bronze Medal","4","5"] +// console.log(findRelativeRanks([10, 3, 8, 9, 4])); // ["Gold Medal","5","Bronze Medal","Silver Medal","4"] +// console.log(findRelativeRanks([1])); // ["Gold Medal"] +// console.log(findRelativeRanks([1, 2])); // ["Silver Medal","Gold Medal"] +// console.log(findRelativeRanks([1, 2, 3])); // ["Bronze Medal","Silver Medal","Gold Medal"] + +function siftDown (array: number[], heapSize: number, rootIndex: number) { + let largest = rootIndex; + let left = 2 * rootIndex + 1; + let right = 2 * rootIndex + 2; + + if (left < heapSize && array[left] < array[largest]) { + largest = left; + } + + if (right < heapSize && array[right] < array[largest]) { + largest = right; + } + + if (largest !== rootIndex) { + [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; + siftDown(array, heapSize, largest); + } +}; + + +function buildMaxHeap (array: number[]) { + const heapSize = array.length; + for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { + siftDown(array, heapSize, i); + } +} + +function heapSort(array: number[]) { + buildMaxHeap(array); + for (let i = array.length - 1; i > 0; i--) { + [array[0], array[i]] = [array[i], array[0]]; + siftDown(array, i, 0); + } +} + +// rewrite the solution using heap +function findRelativeRanksHeap(nums: number[]): string[] { + + const sorted = [...nums]; + heapSort(sorted); + const map = new Map(); + for (let i = 0; i < sorted.length; i++) { + if (i === 0) { + map.set(sorted[i], 'Gold Medal'); + } else if (i === 1) { + map.set(sorted[i], 'Silver Medal'); + } else if (i === 2) { + map.set(sorted[i], 'Bronze Medal'); + } else { + map.set(sorted[i], (i + 1).toString()); + } + } + + return nums.map((num) => map.get(num)!); +} + +console.log(findRelativeRanksHeap([5, 4, 3, 2, 1])); // ["Gold Medal","Silver Medal","Bronze Medal","4","5"] +console.log(findRelativeRanksHeap([10, 3, 8, 9, 4])); // ["Gold Medal","5","Bronze Medal","Silver Medal","4"] +console.log(findRelativeRanksHeap([1])); // ["Gold Medal"] +console.log(findRelativeRanksHeap([1, 2])); // ["Silver Medal","Gold Medal"] +console.log(findRelativeRanksHeap([1, 2, 3])); // ["Bronze Medal","Silver Medal","Gold Medal"] + +// to see the output of this file use the command: npx ts-node src/11-heap/leetcode/relative-ranks.ts \ No newline at end of file From bb8742db3697a5e31c7acd16e9cd4bb108d1f1d5 Mon Sep 17 00:00:00 2001 From: Loiane Date: Mon, 26 Aug 2024 17:59:15 -0400 Subject: [PATCH 38/43] chapter 3 review --- src/03-array/01-arrays.js | 32 ++-- src/03-array/02-adding-removing-elements.js | 94 ++++++------ src/03-array/03-iterator-functions.js | 102 +++++++++---- src/03-array/04-searching-sorting.js | 157 ++++++++++++-------- src/03-array/05-transforming-array.js | 56 ++++--- src/03-array/06-other-methods.js | 66 ++++---- src/03-array/07-multidimensional-arrays.js | 4 +- src/03-array/08-different-data-types.js | 39 +++++ 8 files changed, 351 insertions(+), 199 deletions(-) create mode 100644 src/03-array/08-different-data-types.js diff --git a/src/03-array/01-arrays.js b/src/03-array/01-arrays.js index 0458a07a..cd8bb4a7 100644 --- a/src/03-array/01-arrays.js +++ b/src/03-array/01-arrays.js @@ -28,34 +28,36 @@ console.log('averageTemp[3]', averageTemp[3]); console.log('averageTemp[4]', averageTemp[4]); /* Creating and initializing arrays */ -let daysOfWeek = new Array(); // {1} -daysOfWeek = new Array(7); // {2} -daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3} - // preferred -daysOfWeek = []; // {4} -daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // {5} +let daysOfWeek = []; // creates an empty array +daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + +console.log('daysOfWeek.length:', daysOfWeek.length); // daysOfWeek.length: 7 -console.log('daysOfWeek.length', daysOfWeek.length); // output: 7 +// different syntax, avoid: +daysOfWeek = new Array(); // creates an empty array +daysOfWeek = new Array(7); // pre-defined length of 7 +daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); +// ---- interating over arrays ---- for (let i = 0; i < daysOfWeek.length; i++) { console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); } /* fibonacci numbers */ // Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... -const fibonacci = []; // {1} -fibonacci[1] = 1; // {2} -fibonacci[2] = 1; // {3} +const fibonacci = []; +fibonacci[0] = 0; +fibonacci[1] = 1; -// create the fibonacci sequence starting from the 3rd element -for (let i = 3; i < 20; i++) { - fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; // //{4} +// create the fibonacci sequence starting from the 2nd element +for (let i = 2; i < 20; i++) { + fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; } // display the fibonacci sequence -for (let i = 1; i < fibonacci.length; i++) { // {5} - console.log(`fibonacci[${i}]`, fibonacci[i]); // {6} +for (let i = 1; i < fibonacci.length; i++) { + console.log(`fibonacci[${i}]`, fibonacci[i]); } // instead of {5} and {6} we can use diff --git a/src/03-array/02-adding-removing-elements.js b/src/03-array/02-adding-removing-elements.js index 34fdd30a..fad7f467 100644 --- a/src/03-array/02-adding-removing-elements.js +++ b/src/03-array/02-adding-removing-elements.js @@ -1,73 +1,81 @@ // Path: src/03-array/02-adding-removing-elements.js // @ts-ignore -let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +let raffleTickets = [5, 6, 7, 8, 9]; -numbers[numbers.length] = 10; +raffleTickets[raffleTickets.length] = 10; // using push method -numbers.push(11); -numbers.push(12, 13); +raffleTickets.push(11); +console.log(raffleTickets.push(12, 13)); // 9 + +console.log('raffleTickets: ', raffleTickets); // raffleTickets: [ 5, 6, 7, 8, 9, 10, 11, 12, 13 ] // inserting elements at the beginning -// @ts-ignore -Array.prototype.insertAtBeginning = function (value) { - for (let i = this.length; i >= 0; i--) { - this[i] = this[i - 1]; +const insertAtBeginning = function (array, value) { + for (let i = array.length; i >= 0; i--) { + array[i] = array[i - 1]; } - this[0] = value; + array[0] = value; + return array; }; -// @ts-ignore -numbers.insertAtBeginning(-1); +raffleTickets = insertAtBeginning(raffleTickets, 4); +console.log('raffleTickets: ', raffleTickets); // raffleTickets: [ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] // using unshift method -numbers.unshift(-2); -numbers.unshift(-4, -3); +raffleTickets.unshift(3); +raffleTickets.unshift(0, 1, 2); + +console.log('raffleTickets: ', raffleTickets); // raffleTickets: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] // removing elements from the end -numbers.pop(); // number 13 is removed -// console.log('Removed element: ', numbers.pop()); // Removed element: 13 -console.log('array length: ', numbers.length); // array length: 17 +let winningTicket = raffleTickets.pop(); +console.log('The winning ticket is:', winningTicket); // The winning ticket is: 13 + +console.log('raffleTickets: ', raffleTickets); // raffleTickets: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ] +console.log('array length: ', raffleTickets.length); // array length: 13 // removing elements from the beginning -for (let i = 0; i < numbers.length; i++) { +for (let i = 0; i < raffleTickets.length; i++) { // numbers[i] = numbers[i + 1]; } // removing elements from the beginning - educational purposes only -// @ts-ignore -Array.prototype.reIndex = function (myArray) { - const newArray = []; - for (let i = 0; i < myArray.length; i++) { - if (myArray[i] !== undefined) { - newArray.push(myArray[i]); - } +const removeFirstElement = function(array) { + for (let i = 1; i < array.length; i++) { + array[i - 1] = array[i]; } - return newArray; + array.length--; + return array; } -// remove first position manually and reIndex -// @ts-ignore -Array.prototype.removeFromBeginning = function () { - for (let i = 0; i < this.length; i++) { - this[i] = this[i + 1]; + +// Apply to our raffleTickets array +raffleTickets = removeFirstElement(raffleTickets); +console.log('Updated raffleTickets:', raffleTickets); // Updated raffleTickets: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ] +console.log('array length: ', raffleTickets.length); // array length: 12 + +// alternative way to remove elements from the beginning using a new array +const removeFirstElementWithNewArray = function(array) { + let newArray = []; + for (let i = 1; i < array.length; i++) { + newArray.push(array[i]); } - // @ts-ignore - return this.reIndex(this); -}; -// @ts-ignore -// numbers = numbers.removeFromBeginning(); + return newArray; +} // using shift method -numbers.shift(); -console.log('numbers after shift: ', numbers); -console.log('array length: ', numbers.length); // array length: 16 +raffleTickets.shift(); +console.log('numbers after shift: ', raffleTickets); // numbers after shift: [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ] +console.log('array length: ', raffleTickets.length); // array length: 11 // adding and removing elements from a specific position -// using the splice method -numbers.splice(5, 3); // removes 3 elements starting from the 5th position -console.log('numbers: ', numbers); // numbers: [ -3,β€―-2, -1, 0, 1, 5, 6, 7, 8, 9,β€―10,β€―11, 12 ] +// using the splice method to remove elements +const prizeWinners = raffleTickets.splice(5, 3); // removes 3 elements starting from the 5th position +console.log('prizeWinners: ', prizeWinners); // prizeWinners: [ 7, 8, 9 ] +console.log('remaing tickets: ', raffleTickets); // remaing tickets: [ 2, 3, 4, 5, 6, 10, 11, 12 ] -// adding elements, 2, 3 and 4 at the 5th position -numbers.splice(5, 0, 2, 3, 4); +// adding number 8 and 9 back to the array +raffleTickets.splice(5, 0, 8, 9); // adds 8, 9 back to the array +console.log('raffleTickets: ', raffleTickets); // raffleTickets: [ 2, 3, 4, 5, 6, 8, 9, 10, 11, 12 ] // to see the output of this file use the command: node src/03-array/02-adding-removing-elements.js diff --git a/src/03-array/03-iterator-functions.js b/src/03-array/03-iterator-functions.js index 2ae003b5..ea90f93e 100644 --- a/src/03-array/03-iterator-functions.js +++ b/src/03-array/03-iterator-functions.js @@ -1,45 +1,85 @@ // Path: src/03-array/03-iterator-functions.js -// @ts-ignore -const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - // using forEach method -numbers.forEach((value, index) => { - console.log(`numbers[${index}]`, value); -}); +const raffleParticipants = ['poppy@email.com', 'sera@email.com', 'millie@example.com']; +const sendWelcomeEmail = email => { + console.log(`Welcome email sent to ${email}`); +}; + +raffleParticipants.forEach(email => sendWelcomeEmail(email)); + +raffleParticipants.forEach(sendWelcomeEmail); + +const sendConfirmationEmail = (email, raffleNumber) => { + console.log(`Hi ${email}, your raffle number is ${raffleNumber}`); +}; -numbers.forEach(value => console.log(value)); +const sendSpecialPrizeEmail = (email) => { + console.log(`Congrats ${email}! You've won a special prize!`); +}; + +raffleParticipants.forEach((value, index, array) => { + const raffleNumber = index + 1; // Raffle numbers start from 1 + sendConfirmationEmail(value, raffleNumber); + + if (index === array.length - 1) { // Check if it's the last element + sendSpecialPrizeEmail(value); + } +}); // using every method -const isBelowSeven = numbers.every(value => value < 7); -console.log('All values are below 7?:', isBelowSeven); // false - -// rewriting the preceding code using for loop -let isBelowSevenForLoop = true; -for (let i = 0; i < numbers.length; i++) { - if (numbers[i] >= 7) { - isBelowSevenForLoop = false; - break; +const formFields = [ + { id: 'username', value: 'poppy' }, + { id: 'email', value: 'poppy@email.com' }, + { id: 'password', value: 'bookClub123!' }]; + + const isFormValid = () => + formFields.every(field => field.value !== '' && field.value !== null); + + +const onFormSubmit = () => { + if (isFormValid()) { + console.log('Form submitted successfully!'); + } else { + console.log('Please fill out all required fields.'); } } -console.log('All values are below 7?:', isBelowSevenForLoop); // false +onFormSubmit(); // Form submitted successfully! + +// using a for loop +const isFormValidForLoop = () => { + for (let i = 0; i < formFields.length; i++) { + if (formFields[i].value === '' || formFields[i].value === null) { + return false; + } + } + return true; +}; + // using some method -const isSomeValueBelowSeven = numbers.some(value => value < 7); -console.log('Is any value below 7?:', isSomeValueBelowSeven); // true - -// rewriting the preceding code using for loop -let isSomeValueBelowSevenForLoop = false; -for (let i = 0; i < numbers.length; i++) { - if (numbers[i] < 7) { - isSomeValueBelowSevenForLoop = true; - break; +const products = [ + { name: 'Laptop', inStock: true }, + { name: 'Smartphone', inStock: false }, + { name: 'Headphones', inStock: true } +]; + +const searchQuery = 'phone'; +const isProductAvailable = products.some(product => { + return product.name.toLowerCase().includes(searchQuery.toLowerCase()) && product.inStock; +}); + +console.log(isProductAvailable ? 'Product Available!' : 'No Products Found.'); // Product Available! + +// using a for loop +const isProductAvailableForLoop = () => { + for (let i = 0; i < products.length; i++) { + if (products[i].name.toLowerCase().includes(searchQuery.toLowerCase()) && products[i].inStock) { + return true; // Found a match, no need to continue checking + } } -} + return false; // No match found after checking all products +}; -// using filter method -// @ts-ignore -const valuesBelowSeven = numbers.filter(value => value < 7); -console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] // to see the output of this file use the command: node src/03-array/03-iterator-functions.js \ No newline at end of file diff --git a/src/03-array/04-searching-sorting.js b/src/03-array/04-searching-sorting.js index 25fa2f97..918cc72d 100644 --- a/src/03-array/04-searching-sorting.js +++ b/src/03-array/04-searching-sorting.js @@ -1,30 +1,65 @@ // Path: src/03-array/04-searching-sorting.js -// @ts-ignore -const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - // using indexOf method -console.log('Index of 5:', numbers.indexOf(5)); // 4 -console.log('Index of 11:', numbers.indexOf(11)); // -1 +const shoppingList = ['apples', 'bananas', 'milk', 'eggs', 'bread', 'milk']; + +const itemToFind = 'milk'; +const firstMilkIndex = shoppingList.indexOf(itemToFind); +if (firstMilkIndex !== -1) { + const position = firstMilkIndex + 1; + console.log(`Milk is already on the list at position ${position}`); +} else { + console.log('Milk is not on the list yet.'); +} +// Milk is already on the list at position 3 // using lastIndexOf method -console.log('Last index of 5:', numbers.lastIndexOf(5)); // 4 -console.log('Last index of 11:', numbers.lastIndexOf(11)); // -1 +const lastMilkIndex = shoppingList.lastIndexOf(itemToFind); +if (lastMilkIndex !== -1 && lastMilkIndex !== firstMilkIndex) { // Check if there's a second occurrence + shoppingList.splice(lastMilkIndex, 1); // Remove the last occurrence + console.log('Removed the duplicate "milk" from the list'); +} +// Removed the duplicate "milk" from the list // using includes method -console.log('Is 5 included?:', numbers.includes(5)); // true -console.log('Is 11 included?:', numbers.includes(11)); // false +const newItem = 'cheese'; +if (shoppingList.includes(newItem)) { + console.log('Cheese is already on the list!'); +} else { + shoppingList.push(newItem); + console.log('Cheese added to the list!'); +} +// Cheese added to the list! // using find, findIndex and findLastIndex methods -const firstValueBelowSeven = numbers.find(value => value < 7); -console.log('First value below 7:', firstValueBelowSeven); // 1 -console.log('Index of first value below 7:', numbers.findIndex(value => value < 7)); // 0 -console.log('Index of last value below 7:', numbers.findLastIndex(value => value < 7)); // 5 +// simple example +const availableToPurchase = [1, 13, 14, 15, 16, 17, 18]; +const desiredTicket = availableToPurchase .find(value => value < 7); +console.log('Desired ticket:', desiredTicket); // 1 + +// findIndex example - find the index and add it to the raflleTickets array +let raffleTickets = [ 2, 3, 4, 5, 6, 8, 9, 10, 11, 12 ]; +const ticketIndex = availableToPurchase.findIndex(value => value < 7); +if (ticketIndex !== -1) { + // Find the correct position to insert the ticket in raffleTickets (maintain numerical order) + const pos = raffleTickets.findIndex(value => value > availableToPurchase[ticketIndex]); + + // If no suitable position is found (all existing tickets are smaller), add to the end + const insertPos = (pos === -1) ? raffleTickets.length : pos; + + // Add the ticket to raffleTickets + raffleTickets.splice(insertPos, 0, availableToPurchase[ticketIndex]); + + // Remove the ticket from availableToPurchase + availableToPurchase.splice(ticketIndex, 1); +} + +// books example const books = [ - { id: 1, title: 'The Fellowship of the Ring' }, - { id: 2, title: 'Fourth Wing' }, - { id: 3, title: 'A Court of Thorns and Roses' } + { id: 1, title: 'The Fellowship of the Ring' }, + { id: 2, title: 'Fourth Wing' }, + { id: 3, title: 'A Court of Thorns and Roses' } ]; console.log('Book with id 2:', books.find(book => book.id === 2)); // { id: 2, title: 'Fourth Wing' } console.log('The Hobbit:', books.find(book => book.title === 'The Hobbit')); // undefined @@ -32,45 +67,46 @@ console.log('The Hobbit:', books.find(book => book.title === 'The Hobbit')); // // remove book with id 3 const bookIndex = books.findIndex(book => book.id === 3); if (bookIndex !== -1) { - books.splice(bookIndex, 1); + books.splice(bookIndex, 1); } // using the filter method in the numbers array -// @ts-ignore -const valuesBelowSeven = numbers.filter(value => value < 7); -console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] +const ticketsAvailable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const firstTicket = raffleTickets.find(ticketNumber => ticketNumber < 7); +console.log('First lucky ticket:', firstTicket); // 1 + +const allTickets = raffleTickets.filter(ticketNum => ticketNum < 7); +console.log('All lucky tickets:', allTickets); // [1, 2, 3, 4, 5, 6] + // reverse the array -numbers.reverse(); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] -console.log('Reversed numbers:', numbers); +raffleTickets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +raffleTickets.reverse(); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +console.log('Reversed numbers:', raffleTickets); // sort the array -numbers.sort(); // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9] +raffleTickets.sort(); // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9] -numbers.sort((a, b) => a - b); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +raffleTickets.sort((a, b) => a - b); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // @ts-ignore -function compareNumbers(a, b) { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - // a must be equal to b - return 0; - } - numbers.sort(compareNumbers); - -console.log('Sorted numbers:', numbers); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +function compareNumbers(a, b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; // a must be equal to b +} + +raffleTickets.sort(compareNumbers); + +console.log('Sorted numbers:', raffleTickets); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // custom sorting // @ts-ignore -const friends = [ - { name: 'Frodo', age: 30 }, - { name: 'Violet', age: 18 }, - { name: 'Aelin', age: 20 } -]; +const friends = [ + { name: 'Frodo', age: 30 }, + { name: 'Violet', age: 18 }, + { name: 'Aelin', age: 20 } +]; // @ts-ignore const compareFriends = (friendA, friendB) => friendA.age - friendB.age; friends.sort(compareFriends); @@ -78,28 +114,27 @@ console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { nam // sorting strings // @ts-ignore -let names = ['Ana', 'ana', 'john', 'John']; -console.log(names.sort()); // ['Ana', 'John', 'ana', 'john'] - -names = ['Ana', 'ana', 'john', 'John']; // reset the array to its original state -names.sort((a, b) => { - const nameA = a.toLowerCase(); - const nameB = b.toLowerCase(); - if (nameA < nameB) { - return -1; - } - if (nameA > nameB) { - return 1; - } - return 0; +let playerNames = ['Ana', 'ana', 'john', 'John']; +console.log(playerNames.sort()); // ['Ana', 'John', 'ana', 'john'] + +playerNames = ['Ana', 'ana', 'john', 'John']; // Reset the array +playerNames.sort((a, b) => { + const nameA = a.toLowerCase(); + const nameB = b.toLowerCase(); + if (nameA < nameB) return -1; + if (nameA > nameB) return 1; + return 0; }); -console.log(names); // ['Ana', 'ana', 'John', 'john'] +console.log(playerNames); // ["Ana", "ana", "john", "John"] -names.sort((a, b) => a.localeCompare(b)); -console.log(names); // ['ana', 'Ana', 'john', 'John'] +playerNames.sort((a, b) => a.localeCompare(b)); +console.log(playerNames); // ['ana', 'Ana', 'john', 'John'] -const names2 = ['MaΓ¨ve', 'Maeve']; -console.log(names2.sort((a, b) => a.localeCompare(b))); // ['Maeve', 'MaΓ¨ve'] +let namesWithAccents = ['MaΓ¨ve', 'Maeve']; +console.log(namesWithAccents.sort((a, b) => a.localeCompare(b))); // ['Maeve', 'MaΓ¨ve'] +// without localeCompare +namesWithAccents = ['MaΓ¨ve', 'Maeve']; +console.log(namesWithAccents.sort()); // ['Maeve', 'MaΓ¨ve'] // to see the output of this file use the command: node src/03-array/04-searching-sorting.js \ No newline at end of file diff --git a/src/03-array/05-transforming-array.js b/src/03-array/05-transforming-array.js index e08178a5..fa0ac6a0 100644 --- a/src/03-array/05-transforming-array.js +++ b/src/03-array/05-transforming-array.js @@ -1,16 +1,21 @@ // Path: src/03-array/05-transforming-array.js // @ts-ignore -const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const numbers_ = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // using the map method -const squaredNumbers = numbers.map(value => value * value); -console.log('Squared numbers:', squaredNumbers); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] +const celsiusTemperatures = [0, 5, 10, 15, 20, 25, 30]; +const fahrenheitTemperatures = celsiusTemperatures.map(celsius => { + return (celsius * 9/5) + 32; +}); +console.log('Celsius:', celsiusTemperatures); // Celsius: [0, 5, 10, 15, 20, 25, 30] +console.log('Fahrenheit:', fahrenheitTemperatures); // Fahrenheit: 32, 41, 50, 59, 68, 77, 86] + // rewriting the above code using a loop -const squaredNumbersLoop = []; -for (let i = 0; i < numbers.length; i++) { - squaredNumbersLoop.push(numbers[i] * numbers[i]); +const fahrenheitTemperaturesLoop = []; +for (let i = 0; i < celsiusTemperatures.length; i++) { + fahrenheitTemperaturesLoop.push((celsiusTemperatures[i] * 9/5) + 32); } // using the split method @@ -24,24 +29,39 @@ const namesCSV = names.join(';'); console.log('Names CSV:', namesCSV); // 'Aelin;Gandalf;Violet;Poppy' // using the reduce method -// @ts-ignore -const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; -const sum = numbers.reduce((acc, value) => acc + value, 0); // 55 +const cartItems = [ + { name: 'Laptop', price: 1200 }, + { name: 'Smartphone', price: 500 }, + { name: 'Headphones', price: 100 } +]; + +const totalPrice = cartItems.reduce((accumulator, currentItem) => { + return accumulator + currentItem.price; +}, 0); // Start the accumulator at 0 +console.log('Total Price:', totalPrice); // Total Price: 1800 // rewriting the above code using a loop -let sumLoop = 0; -for (let i = 0; i < numbers.length; i++) { - sumLoop += numbers[i]; +let totalPriceLoop = 0; +for (let i = 0; i < cartItems.length; i++) { + totalPriceLoop += cartItems[i].price; } // using the reduce method to find the maximum value const scores = [30, 70, 85, 90, 100]; -const highestScore = scores.reduce((max, score) => score > max ? score : max, scores[0]); // 100 +const highestScore = scores.reduce((max, score) => { + return score > max ? score : max; +}); +console.log('Highest Score:', highestScore); // Highest Score: 100 + +// using with +const leaderboard = [ + { player: 'Poppy', score: 150 }, + { player: 'Violet', score: 120 }, + { player: 'Tory', score: 90 } +]; -// using reduceRight method -const reversedNumbers = numbers.reduceRight((acc, value) => { - acc.push(value); - return acc; -}, []); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] +const updated = leaderboard.with(1, { player: 'Violet', score: 135 }); +console.log('Leaderboard:', leaderboard); // [{ player: 'Poppy', score: 150 }, { player: 'Violet', score: 120 }, { player: 'Tory', score: 90 }] +console.log('Updated :', updated); // [{ player: 'Poppy', score: 150 }, { player: 'Violet', score: 135 }, { player: 'Tory', score: 90 }] // to see the output of this file use the command: node src/03-array/05-transforming-array.js \ No newline at end of file diff --git a/src/03-array/06-other-methods.js b/src/03-array/06-other-methods.js index 2334d50a..1a2bc0f3 100644 --- a/src/03-array/06-other-methods.js +++ b/src/03-array/06-other-methods.js @@ -7,56 +7,63 @@ console.log(typeof { id: 1 }); // object console.log(typeof [1, 2, 3]); // object console.log(Array.isArray([1, 2, 3])); // true +console.log(Array.isArray({ id: 1 })); // false // real world example -const jsonString = JSON.stringify('[{"id":1,"title":"The Fellowship of the Ring"},{"id":2,"title":"Fourth Wing"}]'); -const dataReceived = JSON.parse(jsonString); +const jsonStringFromAPI = '[{"id":1,"title":"The Lord of the Rings"},{"id":2,"title":"The Hobbit"}]'; +const dataReceived = JSON.parse(jsonStringFromAPI); if (Array.isArray(dataReceived)) { - console.log('It is an array'); - // check if The Fellowship of the Ring is in the array - const fellowship = dataReceived.find((item) => { - return item.title === 'The Fellowship of the Ring'; - }); - if (fellowship) { - console.log('We received the book we were looking for!'); + console.log('Received an array of books!'); + // check if The Hobbit is in the array + const hobbitBook = dataReceived.find(book => book.title === 'The Hobbit'); + if (hobbitBook) { + console.log('Found The Hobbit!'); } else { - console.log('We did not receive the book we were looking for!'); + console.log('The Hobbit was not found in the results.'); } +} else { + console.log('Data received is not an array.'); + // Handle other possible response types (single object, null, etc.) } +// Received an array of books! // using Array.from() method -// @ts-ignore -const numbers = [1, 2, 3, 4, 5]; -const numbersCopy = Array.from(numbers); -console.log(numbersCopy); // [1, 2, 3, 4, 5] +const originalPixels = [255, 0, 0, 128, 128, 128]; // red, gray +const backupPixels = Array.from(originalPixels); +console.log(backupPixels); // [255, 0, 0, 128, 128, 128] + +// increase each pixel value by 50 +const brightenedPixels = Array.from(originalPixels, pixelValue => pixelValue + 50); +console.log(brightenedPixels); // [305, 50, 50, 178, 178, 178] -const evens = Array.from(numbers, x => (x % 2 == 0)); -console.log(evens); // [false, true, false, true, false] // Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. -// @ts-ignore -const friends = [ +let friends = [ { name: 'Frodo', age: 30 }, - { name: 'Violet', age: 18 }, - { name: 'Aelin', age: 20 } + { name: 'Violet', age: 18 } ]; const friendsCopy = Array.from(friends); -console.log(friendsCopy); +friends[0].name = 'Sam'; // modify the original +console.log(friendsCopy[0].name); // 'Sam' -friends[0].name = 'Sam'; -console.log(friendsCopy[0].name); // Sam // deep copy -const friendsDeepCopy = JSON.parse(JSON.stringify(friends)); -friends[0].name = 'Frodo'; -console.log(friendsDeepCopy[0].name); // Sam +const anotherBackup = [...originalPixels]; +const friendsDeepCopy = structuredClone(friends); +friendsDeepCopy[0].name = 'Frodo'; +console.log(friends[0].name); // 'Sam' - dit not change the original array + // using Array.of() method -const numbersArray = Array.of(1, 2, 3, 4, 5); -console.log(numbersArray); // [1, 2, 3, 4, 5] +let suits = Array.of('Hearts', 'Diamonds', 'Clubs', 'Spades'); +console.log(suits); // ['Hearts', 'Diamonds', 'Clubs', 'Spades'] + +// same as: +suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']; -let numbersCopy2 = Array.of(...numbersArray); +const originalDeck = [ 1, 2, 3 ]; +const shuffledDeck = Array.of(...originalDeck); // using Array.fill() method const tornamentResults = new Array(5).fill('pending'); @@ -70,5 +77,6 @@ const positiveNumbers = [1, 2, 3]; const negativeNumbers = [-3, -2, -1]; let allNumbers = negativeNumbers.concat(zero, positiveNumbers); +allNumbers = [...negativeNumbers,zero,...positiveNumbers]; // to see the output of this file use the command: node src/03-array/06-other-methods.js \ No newline at end of file diff --git a/src/03-array/07-multidimensional-arrays.js b/src/03-array/07-multidimensional-arrays.js index 7f28d754..93cc2b3b 100644 --- a/src/03-array/07-multidimensional-arrays.js +++ b/src/03-array/07-multidimensional-arrays.js @@ -1,7 +1,7 @@ // Path: src/03-array/07-multidimensional-arrays.js -let averageTempDay1 = [72, 75, 79, 79, 81, 81]; -let averageTempDay2 = [81, 79, 75, 75, 73, 72]; +let averageHourlyTempDay1 = [72, 75, 79, 79, 81, 81]; +let averageHourlyTempDay2 = [81, 79, 75, 75, 73, 72]; // multidimensional array representation let averageTempMultipleDays = []; diff --git a/src/03-array/08-different-data-types.js b/src/03-array/08-different-data-types.js new file mode 100644 index 00000000..0f6bbc3d --- /dev/null +++ b/src/03-array/08-different-data-types.js @@ -0,0 +1,39 @@ +// Path: src/03-array/07-different-data-types.js + +const legacyOrderData = '12345,Cassian,2024-01-21,Laptop:1200|Mouse:50,1250,Shipped'; +const customerOrderArray = legacyOrderData.split(','); +console.log(customerOrderArray); +// [ +// '12345', +// 'Cassian', +// '2024-01-21', +// 'Laptop:1200|Mouse:50', +// '1250', +// 'Shipped' +// ] + +const customerOrder = { + id: Number(customerOrderArray[0]), + name: customerOrderArray[1], + date: new Date(customerOrderArray[2]), + items: customerOrderArray[3].split('|').map(item => { + const [name, price] = item.split(':'); + return { + name, + price: Number(price) + }; + }), + total: Number(customerOrderArray[4]), + status: customerOrderArray[5] +}; +console.log(customerOrder); +// { +// id: 12345, +// name: 'Cassian', +// date: 2024-01-21T00:00:00.000Z, +// items: [ { name: 'Laptop', price: 1200 }, { name: 'Mouse', price: 50 } ], +// total: 1250, +// status: 'Shipped' +// } + +// to see the output of this file use the command: node src/03-array/07-different-data-types.js \ No newline at end of file From f02966651baf496e66d4faf23b25203e4baa628d Mon Sep 17 00:00:00 2001 From: Loiane Date: Thu, 29 Aug 2024 16:41:01 -0400 Subject: [PATCH 39/43] chapter 12: tries --- src/12-trie/01-spell-checker.js | 53 +++++++++++++ ...ign-add-and-search-words-data-structure.ts | 54 +++++++++++++ .../leetcode/implement-trie-prefix-tree.ts | 62 +++++++++++++++ src/12-trie/leetcode/longest-common-prefix.ts | 66 ++++++++++++++++ src/12-trie/trie.js | 70 +++++++++++++++++ src/12-trie/trie.ts | 77 +++++++++++++++++++ 6 files changed, 382 insertions(+) create mode 100644 src/12-trie/01-spell-checker.js create mode 100644 src/12-trie/leetcode/design-add-and-search-words-data-structure.ts create mode 100644 src/12-trie/leetcode/implement-trie-prefix-tree.ts create mode 100644 src/12-trie/leetcode/longest-common-prefix.ts create mode 100644 src/12-trie/trie.js create mode 100644 src/12-trie/trie.ts diff --git a/src/12-trie/01-spell-checker.js b/src/12-trie/01-spell-checker.js new file mode 100644 index 00000000..f46e452d --- /dev/null +++ b/src/12-trie/01-spell-checker.js @@ -0,0 +1,53 @@ +// src/12-trie/01-spell-checker.js + +const Trie = require('./trie'); + +class SpellChecker { + #trie = new Trie(); + + buildDictionary(words) { + for (let word of words) { + this.#trie.insert(word); + } + } + + isWordInDictionary(word) { + return this.#trie.search(word); + } + + getSuggestions(word) { + const suggestions = []; + const wordArray = word.split(''); + for (let i = 0; i < wordArray.length; i++) { + const temp = wordArray[i]; + wordArray[i] = ''; + const newWord = wordArray.join(''); + if (this.#trie.startsWith(newWord)) { + suggestions.push(newWord); + } + wordArray[i] = temp; + } + return suggestions; + } + + removeWord(word) { + return this.#trie.remove(word); + } +} + +const spellChecker = new SpellChecker(); +spellChecker.buildDictionary(['cat', 'bat', 'rat', 'drat', 'dart', 'date']); +console.log(spellChecker.isWordInDictionary('bat')); // true +console.log(spellChecker.isWordInDictionary('drate')); // false +console.log(spellChecker.getSuggestions('drate')); // [ 'date', 'drat' ] + + +const trie = new Trie(); +trie.insert('she'); +trie.insert('sells'); +trie.insert('sea'); +trie.insert('shells'); +trie.insert('by'); +trie.insert('the'); + +// to see the output of this file use the command: node src/12-trie/01-spell-checker.js \ No newline at end of file diff --git a/src/12-trie/leetcode/design-add-and-search-words-data-structure.ts b/src/12-trie/leetcode/design-add-and-search-words-data-structure.ts new file mode 100644 index 00000000..e9dafd05 --- /dev/null +++ b/src/12-trie/leetcode/design-add-and-search-words-data-structure.ts @@ -0,0 +1,54 @@ +// https://leetcode.com/problems/design-add-and-search-words-data-structure/description/ +// 211. Design Add and Search Words Data Structure + +// @ts-ignore +class TrieNode { + children: Map; + isEndOfWord: boolean; + + constructor() { + this.children = new Map(); + this.isEndOfWord = false; + } +} + +class WordDictionary { + private root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + addWord(word: string): void { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + node.children.set(char, new TrieNode()); + } + node = node.children.get(char)!; + } + node.isEndOfWord = true; + } + + search(word: string): boolean { + return this.searchFromNode(word, this.root); + } + + private searchFromNode(word: string, node: TrieNode): boolean { + for (let i = 0; i < word.length; i++) { + const char = word[i]; + if (char === '.') { + for (let child of node.children.values()) { + if (this.searchFromNode(word.slice(i + 1), child)) { + return true; + } + } + return false; + } else if (!node.children.has(char)) { + return false; + } + node = node.children.get(char)!; + } + return node.isEndOfWord; + } +} \ No newline at end of file diff --git a/src/12-trie/leetcode/implement-trie-prefix-tree.ts b/src/12-trie/leetcode/implement-trie-prefix-tree.ts new file mode 100644 index 00000000..39ac3533 --- /dev/null +++ b/src/12-trie/leetcode/implement-trie-prefix-tree.ts @@ -0,0 +1,62 @@ +// https://leetcode.com/problems/implement-trie-prefix-tree/ +// 208. Implement Trie (Prefix Tree) + + +class TrieNode { + children: Map; + isEndOfWord: boolean; + + constructor() { + this.children = new Map(); + this.isEndOfWord = false; + } +} + +class Trie { + private root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + insert(word: string): void { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + node.children.set(char, new TrieNode()); + } + node = node.children.get(char)!; + } + node.isEndOfWord = true; + } + + search(word: string): boolean { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char)!; + } + return node.isEndOfWord; + } + + startsWith(prefix: string): boolean { + let node = this.root; + for (let char of prefix) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char)!; + } + return true; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ \ No newline at end of file diff --git a/src/12-trie/leetcode/longest-common-prefix.ts b/src/12-trie/leetcode/longest-common-prefix.ts new file mode 100644 index 00000000..26b751fd --- /dev/null +++ b/src/12-trie/leetcode/longest-common-prefix.ts @@ -0,0 +1,66 @@ +// https://leetcode.com/problems/longest-common-prefix/description/ +// 14. Longest Common Prefix + +function longestCommonPrefix(strs: string[]): string { + if (strs.length === 0) { return ''; } + let prefix = strs[0]; + for (let i = 1; i < strs.length; i++) { + while (strs[i].indexOf(prefix) !== 0) { + prefix = prefix.substring(0, prefix.length - 1); + } + } + return prefix; +} + +// @ts-ignore +class TrieNode { + children: Map = new Map(); + isEndOfWord: boolean = false; +} + +// @ts-ignore +class Trie { + private root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + insert(word: string): void { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + node.children.set(char, new TrieNode()); + } + node = node.children.get(char); + } + node.isEndOfWord = true; + } + + findLongestCommonPrefix(): string { + let node = this.root; + let prefix = ''; + while (node.children.size === 1 && !node.isEndOfWord) { + const char = Array.from(node.children.keys())[0]; + prefix += char; + node = node.children.get(char); + } + return prefix; + } +} + +function longestCommonPrefixTrie(strs: string[]): string { + if (strs.length === 0) { + return ''; + } + + let trie = new Trie(); + for (let str of strs) { + trie.insert(str); + } + + return trie.findLongestCommonPrefix(); +} + +console.log(longestCommonPrefix(['flower', 'flow', 'flight'])); // 'fl' +console.log(longestCommonPrefix(['dog', 'racecar', 'car'])); // '' diff --git a/src/12-trie/trie.js b/src/12-trie/trie.js new file mode 100644 index 00000000..d61298a2 --- /dev/null +++ b/src/12-trie/trie.js @@ -0,0 +1,70 @@ +class TrieNode { + constructor() { + this.children = new Map(); + this.isEndOfWord = false; + } +} + +class Trie { + #root = new TrieNode(); + + insert(word) { + let node = this.#root; + for (let char of word) { + if (!node.children.has(char)) { + node.children.set(char, new TrieNode()); + } + node = node.children.get(char); + } + node.isEndOfWord = true; + } + + search(word) { + let node = this.#root; + for (let char of word) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char); + } + return node.isEndOfWord; + } + + startsWith(prefix) { + let node = this.#root; + for (let char of prefix) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char); + } + return true; + } + + remove(word) { + return this.#removeWord(this.#root, word, 0); + } + + #removeWord(node, word, index) { + if (index === word.length) { + if (!node.isEndOfWord) return false; + node.isEndOfWord = false; + return node.children.size === 0; + } + + const char = word[index]; + if (!node.children.has(char)) { + return false; + } + + const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char), word, index + 1); + if (shouldDeleteCurrentNode) { + node.children.delete(char); + return node.children.size === 0; + } + + return false; + } +} + +module.exports = Trie; \ No newline at end of file diff --git a/src/12-trie/trie.ts b/src/12-trie/trie.ts new file mode 100644 index 00000000..080921b8 --- /dev/null +++ b/src/12-trie/trie.ts @@ -0,0 +1,77 @@ +class TrieNode { + children: Map; + isEndOfWord: boolean; + + constructor() { + this.children = new Map(); + this.isEndOfWord = false; + } +} + +class Trie { + private root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + insert(word: string): void { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + node.children.set(char, new TrieNode()); + } + node = node.children.get(char)!; + } + node.isEndOfWord = true; + } + + search(word: string): boolean { + let node = this.root; + for (let char of word) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char)!; + } + return node.isEndOfWord; + } + + startsWith(prefix: string): boolean { + let node = this.root; + for (let char of prefix) { + if (!node.children.has(char)) { + return false; + } + node = node.children.get(char)!; + } + return true; + } + + remove(word: string): boolean { + return this.#removeWord(this.root, word, 0); + } + + #removeWord(node: TrieNode, word: string, index: number): boolean { + if (index === word.length) { + if (!node.isEndOfWord) return false; + node.isEndOfWord = false; + return node.children.size === 0; + } + + const char = word[index]; + if (!node.children.has(char)) { + return false; + } + + const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char)!, word, index + 1); + if (shouldDeleteCurrentNode) { + node.children.delete(char); + return node.children.size === 0; + } + + return false; + } +} + +export default Trie; \ No newline at end of file From 02ed22e127019eddb5693af0c117c91532ece41c Mon Sep 17 00:00:00 2001 From: Loiane Date: Fri, 30 Aug 2024 16:57:11 -0400 Subject: [PATCH 40/43] chapter 4 reviews --- src/04-stack/01-undo-feature.js | 34 +++++++++ src/04-stack/01-using-stack-class.js | 34 --------- ...mples.js => 02-base-converter-examples.js} | 0 ...mples.ts => 02-base-converter-examples.ts} | 0 src/04-stack/02-using-stack-object-class.js | 44 ----------- src/04-stack/02-using-stack-object-class.ts | 48 ------------ src/04-stack/decimal-to-base.js | 6 ++ src/04-stack/decimal-to-binary.js | 10 +-- src/04-stack/stack-object.js | 74 ------------------- src/04-stack/stack-object.ts | 72 ------------------ 10 files changed, 45 insertions(+), 277 deletions(-) create mode 100644 src/04-stack/01-undo-feature.js delete mode 100644 src/04-stack/01-using-stack-class.js rename src/04-stack/{03-base-converter-examples.js => 02-base-converter-examples.js} (100%) rename src/04-stack/{03-base-converter-examples.ts => 02-base-converter-examples.ts} (100%) delete mode 100644 src/04-stack/02-using-stack-object-class.js delete mode 100644 src/04-stack/02-using-stack-object-class.ts delete mode 100644 src/04-stack/stack-object.js delete mode 100644 src/04-stack/stack-object.ts diff --git a/src/04-stack/01-undo-feature.js b/src/04-stack/01-undo-feature.js new file mode 100644 index 00000000..c4ada225 --- /dev/null +++ b/src/04-stack/01-undo-feature.js @@ -0,0 +1,34 @@ +// src/04-stack/01-undo-feature.js + +const Stack = require('./stack'); +// import { Stack } from './stack'; // or './stack.js' if you are using ES modules + +const undoFeature = new Stack(); + +console.log(undoFeature.isEmpty()); // true + +undoFeature.push({action: 'typing', text: 'S'}); +undoFeature.push({action: 'typing', text: 't'}); + +console.log(undoFeature.peek()); // { action: 'typing', text: 't' } + +console.log(undoFeature.size); // 2 + +undoFeature.push({action: 'typing', text: 'a'}); +undoFeature.push({action: 'typing', text: 'c'}); +undoFeature.push({action: 'typing', text: 'k'}); + +console.log(undoFeature.size); // 5 +console.log(undoFeature.isEmpty()); // false + +// removing two elements from the stack +undoFeature.pop(); +undoFeature.pop(); + +console.log(undoFeature.size); // 3 +console.log(undoFeature.peek()); // { action: 'typing', text: 'a' } + +// toString +console.log(undoFeature.toString()); + +// to see the output of this file use the command: node src/04-stack/01-undo-feature.js \ No newline at end of file diff --git a/src/04-stack/01-using-stack-class.js b/src/04-stack/01-using-stack-class.js deleted file mode 100644 index a088d46e..00000000 --- a/src/04-stack/01-using-stack-class.js +++ /dev/null @@ -1,34 +0,0 @@ -// src/04-stack/01-using-stack-class.js - -const Stack = require('./stack'); -// import { Stack } from './stack'; // or './stack.js' if you are using ES modules - -const stack = new Stack(); - -console.log(stack.isEmpty()); // true - -stack.push({action: 'typing', text: 'S'}); -stack.push({action: 'typing', text: 't'}); - -console.log(stack.peek()); // { action: 'typing', text: 't' } - -console.log(stack.size); // 2 - -stack.push({action: 'typing', text: 'a'}); -stack.push({action: 'typing', text: 'c'}); -stack.push({action: 'typing', text: 'k'}); - -console.log(stack.size); // 5 -console.log(stack.isEmpty()); // false - -// removing two elements from the stack -stack.pop(); -stack.pop(); - -console.log(stack.size); // 3 -console.log(stack.peek()); // { action: 'typing', text: 'a' } - -// toString -console.log(stack.toString()); - -// to see the output of this file use the command: node src/04-stack/01-using-stack-class.js \ No newline at end of file diff --git a/src/04-stack/03-base-converter-examples.js b/src/04-stack/02-base-converter-examples.js similarity index 100% rename from src/04-stack/03-base-converter-examples.js rename to src/04-stack/02-base-converter-examples.js diff --git a/src/04-stack/03-base-converter-examples.ts b/src/04-stack/02-base-converter-examples.ts similarity index 100% rename from src/04-stack/03-base-converter-examples.ts rename to src/04-stack/02-base-converter-examples.ts diff --git a/src/04-stack/02-using-stack-object-class.js b/src/04-stack/02-using-stack-object-class.js deleted file mode 100644 index 11e57dd3..00000000 --- a/src/04-stack/02-using-stack-object-class.js +++ /dev/null @@ -1,44 +0,0 @@ -// src/04-stack/02-using-stack-object-class.js - -const Stack = require('./stack-object'); -// import { Stack } from './stack-object'; // or './stack-object.js' if you are using ES modules - -const stack = new Stack(); - -console.log(stack.isEmpty()); // true - -console.log(stack.peek()); // undefined - -stack.push({action: 'typing', text: 'S'}); -stack.push({action: 'typing', text: 't'}); - -// internal object representation -// #items = { -// 0: { action: 'typing', text: 'S' }, -// 1: { action: 'typing', text: 't' }} -// }; -// #count = 2; - -console.log(stack.peek()); // { action: 'typing', text: 't' } - -console.log(stack.size); // 2 - -stack.push({action: 'typing', text: 'a'}); -stack.push({action: 'typing', text: 'c'}); -stack.push({action: 'typing', text: 'k'}); - -console.log(stack.size); // 5 -console.log(stack.isEmpty()); // false - -// removing two elements from the stack -stack.pop(); -stack.pop(); - -console.log(stack.size); // 3 -console.log(stack.peek()); // { action: 'typing', text: 'a' } - -// toString -console.log(stack); - - -// to see the output of this file use the command: node src/04-stack/02-using-stack-object-class.js \ No newline at end of file diff --git a/src/04-stack/02-using-stack-object-class.ts b/src/04-stack/02-using-stack-object-class.ts deleted file mode 100644 index 3b5cf53a..00000000 --- a/src/04-stack/02-using-stack-object-class.ts +++ /dev/null @@ -1,48 +0,0 @@ -// src/04-stack/02-using-stack-object-class.ts - -import Stack from './stack-object'; - -interface EditorAction { - action: string; - text: string; -} - -const stack = new Stack(); - -console.log(stack.isEmpty()); // true - -console.log(stack.peek()); // undefined - -stack.push({action: 'typing', text: 'S'}); -stack.push({action: 'typing', text: 't'}); - -// internal object representation -// #items = { -// 0: { action: 'typing', text: 'S' }, -// 1: { action: 'typing', text: 't' }} -// }; -// #count = 2; - -console.log(stack.peek()); // { action: 'typing', text: 't' } - -console.log(stack.size); // 2 - -stack.push({action: 'typing', text: 'a'}); -stack.push({action: 'typing', text: 'c'}); -stack.push({action: 'typing', text: 'k'}); - -console.log(stack.size); // 5 -console.log(stack.isEmpty()); // false - -// removing two elements from the stack -stack.pop(); -stack.pop(); - -console.log(stack.size); // 3 -console.log(stack.peek()); // { action: 'typing', text: 'a' } - -// toString -console.log(stack); - - -// to see the output of this file use the command: npx ts-node src/04-stack/02-using-stack-object-class.js \ No newline at end of file diff --git a/src/04-stack/decimal-to-base.js b/src/04-stack/decimal-to-base.js index 238e1e23..d3644866 100644 --- a/src/04-stack/decimal-to-base.js +++ b/src/04-stack/decimal-to-base.js @@ -14,6 +14,12 @@ function decimalToBase(decimalNumber, base) { throw new Error('Base must be between 2 and 36'); } + if (decimalNumber === 0) { return '0'; } + + if (decimalNumber < 0) { + throw new Error('Negative numbers are not supported'); + } + const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Digits for base 36 const remainderStack = new Stack(); let baseString = ''; diff --git a/src/04-stack/decimal-to-binary.js b/src/04-stack/decimal-to-binary.js index e1bc4616..37f62656 100644 --- a/src/04-stack/decimal-to-binary.js +++ b/src/04-stack/decimal-to-binary.js @@ -15,13 +15,13 @@ function decimalToBinary(decimalNumber) { return '0'; } - while (decimalNumber > 0) { // {1} - const remainder = Math.floor(decimalNumber % 2); // {2} - remainderStack.push(remainder); // {3} - decimalNumber = Math.floor(decimalNumber / 2); // {4} + while (decimalNumber > 0) { + const remainder = Math.floor(decimalNumber % 2); + remainderStack.push(remainder); + decimalNumber = Math.floor(decimalNumber / 2); } - while (!remainderStack.isEmpty()) { // {5} + while (!remainderStack.isEmpty()) { binaryString += remainderStack.pop().toString(); } diff --git a/src/04-stack/stack-object.js b/src/04-stack/stack-object.js deleted file mode 100644 index a216cda3..00000000 --- a/src/04-stack/stack-object.js +++ /dev/null @@ -1,74 +0,0 @@ -// src/04-stack/stack-object.js - -// @ts-ignore -class Stack { - - // private properties - #items = {}; // {1} - #count = 0; // {2} - - push(item) { - this.#items[this.#count] = item; - this.#count++; - } - - pop() { - if (this.isEmpty()) { // {1} - return undefined; - } - this.#count--; // {2} - const result = this.#items[this.#count]; // {3} - delete this.#items[this.#count]; // {4} - return result; // {5} - } - - /** - * - * @returns the last element in the stack or undefined if the stack is empty - */ - peek() { - return this.#items[this.#count - 1]; - } - - isEmpty() { - return this.#count === 0; - } - - get size() { - return this.#count; - } - - clear() { - /* while (!this.isEmpty()) { - this.pop(); - } */ - this.#items = {}; - this.#count = 0; - } - - /** - * Returns a string representation of the stack, bottom to top, without modifying the stack - */ - toString() { - if (this.isEmpty()) { - return 'Empty Stack'; - } - let objString = this.#itemToString(this.#items[0]); // {1} - for (let i = 1; i < this.#count; i++) { // {2} - objString = `${objString}, ${this.#itemToString(this.#items[i])}`; // {3} - } - return objString; - } - - #itemToString(item) { // {4} - if (typeof item === 'object' && item !== null) { - return JSON.stringify(item); // Handle objects - } else { - return item.toString(); // Handle other types - } - } -} - -// export node module so it can be used in different files -// CommonJS export -module.exports = Stack; \ No newline at end of file diff --git a/src/04-stack/stack-object.ts b/src/04-stack/stack-object.ts deleted file mode 100644 index 47d1b963..00000000 --- a/src/04-stack/stack-object.ts +++ /dev/null @@ -1,72 +0,0 @@ -// src/04-stack/stack-object.ts - -class Stack { - - // private properties - private items = {}; // {1} - private count = 0; // {2} - - push(item: T) { - this.items[this.count] = item; - this.count++; - } - - pop(): T | undefined { - if (this.isEmpty()) { // {1} - return undefined; - } - this.count--; // {2} - const result = this.items[this.count]; // {3} - delete this.items[this.count]; // {4} - return result; // {5} - } - - /** - * - * @returns the last element in the stack or undefined if the stack is empty - */ - peek(): T | undefined { - return this.items[this.count - 1]; - } - - isEmpty() { - return this.count === 0; - } - - get size() { - return this.count; - } - - clear() { - /* while (!this.isEmpty()) { - this.pop(); - } */ - this.items = {}; - this.count = 0; - } - - /** - * Returns a string representation of the stack, bottom to top, without modifying the stack - */ - toString() { - if (this.isEmpty()) { - return 'Empty Stack'; - } - let objString = this.itemToString(this.items[0]); // {1} - for (let i = 1; i < this.count; i++) { // {2} - objString = `${objString}, ${this.itemToString(this.items[i])}`; // {3} - } - return objString; - } - - private itemToString(item): string { // {4} - if (typeof item === 'object' && item !== null) { - return JSON.stringify(item); // Handle objects - } else { - return item.toString(); // Handle other types - } - } -} - -// export node module so it can be used in different files -export default Stack; \ No newline at end of file From 6d1b9f2dcb8de60089ce49b82c638c4a384803a1 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 6 Oct 2024 12:40:55 -0400 Subject: [PATCH 41/43] chapter 4 review --- src/04-stack/01-using-stack-class.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/04-stack/01-using-stack-class.ts b/src/04-stack/01-using-stack-class.ts index 0f6d58ae..4d8a31e3 100644 --- a/src/04-stack/01-using-stack-class.ts +++ b/src/04-stack/01-using-stack-class.ts @@ -3,12 +3,13 @@ import Stack from './stack'; enum Action { - TYPE = 'typing' + TYPE = 'typing', + // Add more actions like DELETE, FORMAT, etc. } interface EditorAction { action: Action; - text: string; + text?: string; // Optional text for typing actions } const stack = new Stack(); From 93ce3993e4f512d6dc0f905436987c983f00c755 Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 27 Oct 2024 09:48:48 -0400 Subject: [PATCH 42/43] chapter 13: graphs --- src/13-graph/01-airline-system.js | 31 ++++++++ src/13-graph/02-using-bfs.js | 87 ++++++++++++++++++++++ src/13-graph/03-using-dfs.js | 70 +++++++++++++++++ src/13-graph/04-using-dijkstra.js | 51 +++++++++++++ src/13-graph/05-using-floyd-warshall.js | 42 +++++++++++ src/13-graph/06-using-prim.js | 21 ++++++ src/13-graph/07-using-kruskal.js | 22 ++++++ src/13-graph/bfs.js | 77 +++++++++++++++++++ src/13-graph/dfs.js | 83 +++++++++++++++++++++ src/13-graph/dijkstra.js | 54 ++++++++++++++ src/13-graph/floyd-warshall.js | 41 ++++++++++ src/13-graph/graph.js | 55 ++++++++++++++ src/13-graph/kruskal.js | 69 +++++++++++++++++ src/13-graph/leetcode/course-schedule.ts | 56 ++++++++++++++ src/13-graph/leetcode/number-of-islands.ts | 52 +++++++++++++ src/13-graph/prim.js | 47 ++++++++++++ 16 files changed, 858 insertions(+) create mode 100644 src/13-graph/01-airline-system.js create mode 100644 src/13-graph/02-using-bfs.js create mode 100644 src/13-graph/03-using-dfs.js create mode 100644 src/13-graph/04-using-dijkstra.js create mode 100644 src/13-graph/05-using-floyd-warshall.js create mode 100644 src/13-graph/06-using-prim.js create mode 100644 src/13-graph/07-using-kruskal.js create mode 100644 src/13-graph/bfs.js create mode 100644 src/13-graph/dfs.js create mode 100644 src/13-graph/dijkstra.js create mode 100644 src/13-graph/floyd-warshall.js create mode 100644 src/13-graph/graph.js create mode 100644 src/13-graph/kruskal.js create mode 100644 src/13-graph/leetcode/course-schedule.ts create mode 100644 src/13-graph/leetcode/number-of-islands.ts create mode 100644 src/13-graph/prim.js diff --git a/src/13-graph/01-airline-system.js b/src/13-graph/01-airline-system.js new file mode 100644 index 00000000..d7586724 --- /dev/null +++ b/src/13-graph/01-airline-system.js @@ -0,0 +1,31 @@ +// src/13-graph/01-airline-system.js + +// import the Graph class +const Graph = require('./graph'); + +const airline = new Graph(); + +const airports = 'MCO TPA JFK LAX SFO'.split(' '); + +airports.forEach(airport => + airline.addVertex(airport) +); + +airline.addEdge('MCO', 'JFK'); +airline.addEdge('MCO', 'LAX'); +airline.addEdge('TPA', 'JFK'); +airline.addEdge('TPA', 'LAX'); +airline.addEdge('JFK', 'LAX'); +airline.addEdge('JFK', 'SFO'); +airline.addEdge('LAX', 'SFO'); + +console.log(airline.toString()); + +// Output: +// MCO -> JFK LAX +// TPA -> JFK LAX +// JFK -> MCO TPA LAX SFO +// LAX -> MCO TPA JFK SFO +// SFO -> JFK LAX + +// to see the output of this file use the command: node src/13-graph/01-airline-system.js \ No newline at end of file diff --git a/src/13-graph/02-using-bfs.js b/src/13-graph/02-using-bfs.js new file mode 100644 index 00000000..33cc8a44 --- /dev/null +++ b/src/13-graph/02-using-bfs.js @@ -0,0 +1,87 @@ +// src/13-graph/02-using-bfs.js + +const Graph = require('./graph'); +const { breadthFirstSearch, bfsShortestPath} = require('./bfs'); + +const airline = new Graph(); + +const airports = 'SEA SFO LAX LAS DEN DFW STL MDW JFK ATL MCO MIA'.split(' '); + +airports.forEach(airport => + airline.addVertex(airport) +); + +airline.addEdge('SEA', 'SFO'); +airline.addEdge('SEA', 'DEN'); +airline.addEdge('SEA', 'MDW'); +airline.addEdge('SFO', 'LAX'); +airline.addEdge('LAX', 'LAS'); +airline.addEdge('DEN', 'DFW'); +airline.addEdge('DEN', 'STL'); +airline.addEdge('STL', 'MDW'); +airline.addEdge('STL', 'MCO'); +airline.addEdge('MDW', 'ATL'); +airline.addEdge('MDW', 'JFK'); +airline.addEdge('ATL', 'JFK'); +airline.addEdge('JFK', 'MCO'); +airline.addEdge('JFK', 'MIA'); + +console.log(airline.toString()); + +// Output: +// SEA -> SFO DEN MDW +// SFO -> SEA LAX +// LAX -> SFO LAS +// LAS -> LAX +// DEN -> SEA DFW STL +// DFW -> DEN +// STL -> DEN MDW MCO +// MDW -> SEA STL ATL JFK +// JFK -> MDW ATL MCO MIA +// ATL -> MDW JFK +// MCO -> STL JFK +// MIA -> JFK + +console.log('--- BFS ---'); + +const printAirport = (value) => console.log('Visited airport: ' + value); + +breadthFirstSearch(airline,'SEA', printAirport); + +// Output: +// --- BFS --- +// Visited airport: SEA +// Visited airport: SFO +// Visited airport: DEN +// Visited airport: MDW +// Visited airport: LAX +// Visited airport: DFW +// Visited airport: STL +// Visited airport: ATL +// Visited airport: JFK +// Visited airport: LAS +// Visited airport: MCO +// Visited airport: MIA + +console.log('--- BFS Shortest Path ---'); + +const shortestPath = bfsShortestPath(airline, 'SEA'); +console.log(shortestPath); + +// { distances: { SEA: 0, SFO: 1, LAX: 2, LAS: 3, DEN: 1, DFW: 2, STL: 2, MDW: 1, JFK: 2, ATL: 2, MCO: 3, MIA: 3 }, +// predecessors: { SEA: null, SFO: 'SEA', LAX: 'SFO', LAS: 'LAX', DEN: 'SEA', DFW: 'DEN', STL: 'DEN', MDW: 'SEA', JFK: 'MDW', ATL: 'MDW', MCO: 'STL', MIA: 'JFK'} +// } + +// find the shortest path from SEA to JFK +let path = []; +for (let v = 'JFK'; v !== 'SEA'; v = shortestPath.predecessors[v]) { + path.push(v); +} +path.push('SEA'); +path.reverse(); + +console.log('Shortest path from SEA to JFK: ' + path.join(' -> ')); + +// Shortest path from SEA to JFK: SEA -> MDW -> JFK + +// to see the output of this file use the command: node src/13-graph/02-using-bfs.js \ No newline at end of file diff --git a/src/13-graph/03-using-dfs.js b/src/13-graph/03-using-dfs.js new file mode 100644 index 00000000..981313eb --- /dev/null +++ b/src/13-graph/03-using-dfs.js @@ -0,0 +1,70 @@ +// src/13-graph/03-using-dfs.js + +const Graph = require('./graph'); +const { depthFirstSearch, enhancedDepthFirstSearch } = require('./dfs'); + +const caveSystem = new Graph(); + +const caves = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; + +caves.forEach(cave => caveSystem.addVertex(cave)); + + +caveSystem.addEdge('A', 'B'); +caveSystem.addEdge('A', 'C'); +caveSystem.addEdge('A', 'D'); +caveSystem.addEdge('C', 'D'); +caveSystem.addEdge('C', 'G'); +caveSystem.addEdge('D', 'G'); +caveSystem.addEdge('D', 'H'); +caveSystem.addEdge('B', 'E'); +caveSystem.addEdge('B', 'F'); +caveSystem.addEdge('E', 'I'); + +console.log('********* DFS - cave ***********'); +depthFirstSearch(caveSystem, (cave) => console.log('Visited cave:',cave)); + +// ********* DFS - cave *********** +// Visited cave: A +// Visited cave: B +// Visited cave: E +// Visited cave: I +// Visited cave: F +// Visited cave: C +// Visited cave: D +// Visited cave: G +// Visited cave: H + +console.log('********* Enhanced DFS - cave ***********'); +const result = enhancedDepthFirstSearch(caveSystem); + +console.log(result); + +console.log('********* Topological sort using DFS ***********'); + +const tasks = new Graph(true); // this is a directed graph +['A', 'B', 'C', 'D', 'E', 'F'].forEach(task => tasks.addVertex(task)); +// add the arrows, task dependencies: +tasks.addEdge('A', 'C'); +tasks.addEdge('A', 'D'); +tasks.addEdge('B', 'D'); +tasks.addEdge('B', 'E'); +tasks.addEdge('C', 'F'); +tasks.addEdge('F', 'E'); + +// DFS traversal +const dfsTasks = enhancedDepthFirstSearch(tasks); +console.log(dfsTasks); +// { +// discovery: { A: 1, B: 11, C: 2, D: 8, E: 4, F: 3 }, +// finished: { A: 10, B: 12, C: 7, D: 9, E: 5, F: 6 }, +// predecessors: { A: null, B: null, C: 'A', D: 'A', E: 'F', F: 'C' } +// } + +// sort tasks in decreasing order of finish time +// dfsTasks.finished = { A: 10, B: 12, C: 7, D: 9, E: 5, F: 6 } +const sortedTasks = Object.keys(dfsTasks.finished).sort((a, b) => dfsTasks.finished[b] - dfsTasks.finished[a]); +console.log(sortedTasks); +// [ 'B', 'A', 'D', 'C', 'F', 'E' ] + +// to see the output of this file use the command: node src/13-graph/03-using-dfs.js \ No newline at end of file diff --git a/src/13-graph/04-using-dijkstra.js b/src/13-graph/04-using-dijkstra.js new file mode 100644 index 00000000..2992b251 --- /dev/null +++ b/src/13-graph/04-using-dijkstra.js @@ -0,0 +1,51 @@ +// src/13-graph/04-using-dijkstra.js + +const Graph = require('./graph'); +const dijkstra = require('./dijkstra'); + +const flightCosts = [ +// SEA, MDW, DEN, MCO, STL, JFK, ATL + [0, 300, 220, 1000, 0, 0, 0], // SEA + [300, 0, 0, 0, 50, 210, 190], // MDW + [220, 0, 0, 0, 350, 0, 0], // DEN + [1000, 0, 0, 0, 150, 250, 0], // MCO + [0, 50, 350, 150, 0, 0, 0], // STL + [0, 210, 0, 250, 0, 0, 200], // JFK + [0, 190, 0, 0, 0, 200, 0], // ATL +]; + +console.log('********* Dijkstra\'s Algorithm - Shortest Path ***********'); + +const prices = dijkstra(flightCosts, 0); // SEA +// prices output: +// { +// distances: [ +// 0, 300, 220, +// 500, 350, 510, +// 490 +// ], +// predecessors: { +// '0': [ 0 ], +// '1': [ 0, 1 ], +// '2': [ 0, 2 ], +// '3': [ 0, 1, 4, 3 ], +// '4': [ 0, 1, 4 ], +// '5': [ 0, 1, 5 ], +// '6': [ 0, 1, 6 ] +// } +// } +console.log('Prices from SEA to all airports:'); +const airports = ['SEA', 'MDW', 'DEN', 'MCO', 'STL', 'JFK', 'ATL']; +for (let i = 1; i < airports.length; i++) { + const flights = prices.predecessors[i].map(airport => airports[airport]).join(' -> '); + console.log(`SEA -> ${airports[i]}: $${prices.distances[i]} via ${flights}`); +} +// Prices from SEA to all airports: +// SEA -> MDW: $300 via SEA -> MDW +// SEA -> DEN: $220 via SEA -> DEN +// SEA -> MCO: $500 via SEA -> MDW -> STL -> MCO +// SEA -> STL: $350 via SEA -> MDW -> STL +// SEA -> JFK: $510 via SEA -> MDW -> JFK +// SEA -> ATL: $490 via SEA -> MDW -> ATL + +// to see the output of this file use the command: node src/13-graph/04-using-dijkstra.js diff --git a/src/13-graph/05-using-floyd-warshall.js b/src/13-graph/05-using-floyd-warshall.js new file mode 100644 index 00000000..688c6b84 --- /dev/null +++ b/src/13-graph/05-using-floyd-warshall.js @@ -0,0 +1,42 @@ +// src/13-graph/05-using-floyd-warshall.js + +const floydWarshall = require('./floyd-warshall'); + +const INF = Infinity; +const flightCosts = [ +// SEA, MDW, DEN, MCO, STL, JFK, ATL + [INF, 300, 220, 1000, INF, INF, INF], // SEA + [300, INF, INF, INF, 50, 210, 190], // MDW + [220, INF, INF, INF, 350, INF, INF], // DEN + [1000, INF, INF, INF, 150, 250, INF], // MCO + [INF, 50, 350, 150, INF, INF, INF], // STL + [INF, 210, INF, 250, INF, INF, 200], // JFK + [INF, 190, INF, INF, INF, 200, INF], // ATL +]; + +console.log('********* Floyd-Warshall Algorithm - Shortest Path ***********'); + +const distances = floydWarshall(flightCosts); +console.log('Distances between all airports:'); + +const airports = ['SEA', 'MDW', 'DEN', 'MCO', 'STL', 'JFK', 'ATL']; +console.table(airports.map((airport, i) => { + return airports.reduce((acc, dest, j) => { + acc[dest] = distances[i][j]; + return acc; + }, {}); +})); + +// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β” +// β”‚ Airport β”‚ SEA β”‚ MDW β”‚ DEN β”‚ MCO β”‚ STL β”‚ JFK β”‚ ATL β”‚ +// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€ +// β”‚ SEA β”‚ 0 β”‚ 300 β”‚ 220 β”‚ 500 β”‚ 350 β”‚ 510 β”‚ 490 β”‚ +// β”‚ MDW β”‚ 300 β”‚ 0 β”‚ 400 β”‚ 200 β”‚ 50 β”‚ 210 β”‚ 190 β”‚ +// β”‚ DEN β”‚ 220 β”‚ 400 β”‚ 0 β”‚ 500 β”‚ 350 β”‚ 610 β”‚ 590 β”‚ +// β”‚ MCO β”‚ 500 β”‚ 200 β”‚ 500 β”‚ 0 β”‚ 150 β”‚ 250 β”‚ 390 β”‚ +// β”‚ STL β”‚ 350 β”‚ 50 β”‚ 350 β”‚ 150 β”‚ 0 β”‚ 260 β”‚ 240 β”‚ +// β”‚ JFK β”‚ 510 β”‚ 210 β”‚ 610 β”‚ 250 β”‚ 260 β”‚ 0 β”‚ 200 β”‚ +// β”‚ ATL β”‚ 490 β”‚ 190 β”‚ 590 β”‚ 390 β”‚ 240 β”‚ 200 β”‚ 0 β”‚ +// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜ + +// to see the output of this file use the command: node src/13-graph/05-using-floyd-warshall.js \ No newline at end of file diff --git a/src/13-graph/06-using-prim.js b/src/13-graph/06-using-prim.js new file mode 100644 index 00000000..52a7c00a --- /dev/null +++ b/src/13-graph/06-using-prim.js @@ -0,0 +1,21 @@ +// src/13-graph/06-using-prim.js + +const prim = require('./prim'); + +const cityNames = ['Erebor', 'Rivendell', 'Hobbiton', 'Isengard', 'Rohan', 'Lothlorien']; + +const cities = [ + [0, 2, 4, 0, 0, 0], + [2, 0, 2, 4, 2, 0], + [4, 2, 0, 0, 3, 0], + [0, 4, 0, 0, 3, 2], + [0, 2, 3, 3, 0, 2], + [0, 0, 0, 2, 2, 0] +]; + +const mst = prim(cities); +console.log('Minimum Spanning Tree:', mst); + +// [ -1, 0, 1, 5, 1, 4 ] + +// to see the output of this file use the command: node src/13-graph/06-using-prim.js \ No newline at end of file diff --git a/src/13-graph/07-using-kruskal.js b/src/13-graph/07-using-kruskal.js new file mode 100644 index 00000000..1e67340b --- /dev/null +++ b/src/13-graph/07-using-kruskal.js @@ -0,0 +1,22 @@ +// src/13-graph/07-using-kruskal.js + +const kruskal = require('./kruskal'); + +const cityNames = ['Erebor', 'Rivendell', 'Hobbiton', 'Isengard', 'Rohan', 'Lothlorien']; + +const cities = [ + [0, 2, 4, 0, 0, 0], + [2, 0, 2, 4, 2, 0], + [4, 2, 0, 0, 3, 0], + [0, 4, 0, 0, 3, 2], + [0, 2, 3, 3, 0, 2], + [0, 0, 0, 2, 2, 0] +]; + +const mst = kruskal(cities); + +console.log('Minimum Spanning Tree with Kruskal:', mst); + +// [ [ 0, 1 ], [ 1, 2 ], [ 1, 4 ], [ 3, 5 ], [ 4, 5 ] ] + +// to see the output of this file use the command: node src/13-graph/07-using-kruskal.js \ No newline at end of file diff --git a/src/13-graph/bfs.js b/src/13-graph/bfs.js new file mode 100644 index 00000000..bcfb80c0 --- /dev/null +++ b/src/13-graph/bfs.js @@ -0,0 +1,77 @@ +// src/13-graph/bfs.js + +const Colors = { + WHITE: 0, + GREY: 1, + BLACK: 2 +}; + +const initializeColor = vertices => { + const color = {}; + vertices.forEach(vertex => { + color[vertex] = Colors.WHITE; + }); + return color; +}; + +const breadthFirstSearch = (graph, startVertex, callback) => { + const vertices = graph.vertices; + const adjList = graph.adjList; + const color = initializeColor(vertices); + const queue = [startVertex]; + + while (queue.length) { + const visiting = queue.shift(); + const neighbors = adjList.get(visiting); + color[visiting] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i]; + if (color[neighbor] === Colors.WHITE) { + color[neighbor] = Colors.GREY; + queue.push(neighbor); + } + } + color[visiting] = Colors.BLACK; + if (callback) { + callback(visiting); + } + } +}; + +const bfsShortestPath = (graph, startVertex) => { + const vertices = graph.vertices; + const adjList = graph.adjList; + const color = {}; + const dist = {}; + const pred = {}; + const queue = [startVertex]; + + vertices.forEach(vertex => { + color[vertex] = Colors.WHITE; + dist[vertex] = 0; + pred[vertex] = null; + }); + + while (queue.length) { + const visiting = queue.shift(); + const neighbors = adjList.get(visiting); + color[visiting] = Colors.GREY; + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i]; + if (color[neighbor] === Colors.WHITE) { + color[neighbor] = Colors.GREY; + dist[neighbor] = dist[visiting] + 1; + pred[neighbor] = visiting; + queue.push(neighbor); + } + } + color[visiting] = Colors.BLACK; + } + + return { + distances: dist, + predecessors: pred + }; +}; + +module.exports = { breadthFirstSearch , bfsShortestPath }; \ No newline at end of file diff --git a/src/13-graph/dfs.js b/src/13-graph/dfs.js new file mode 100644 index 00000000..51638b0b --- /dev/null +++ b/src/13-graph/dfs.js @@ -0,0 +1,83 @@ +// src/13-graph/dfs.js + +const Colors = { + WHITE: 0, + GREY: 1, + BLACK: 2 +}; + +const initializeColor = vertices => { + const color = {}; + vertices.forEach(vertex => { + color[vertex] = Colors.WHITE; + }); + return color; +} + +const depthFirstSearch = (graph, callback) => { + const vertices = graph.vertices; + const adjList = graph.adjList; + const color = initializeColor(vertices); + + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + depthFirstSearchVisit(vertices[i], color, adjList, callback); + } + } +} + +const depthFirstSearchVisit = (vertex, color, adjList, callback) => { + color[vertex] = Colors.GREY; // Mark as discovered + if (callback) { + callback(vertex); + } + const neighbors = adjList.get(vertex); + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i]; + if (color[neighbor] === Colors.WHITE) { // If unvisited - recursive call + depthFirstSearchVisit(neighbor, color, adjList, callback); + } + } + color[vertex] = Colors.BLACK; // Mark as explored +} + +const enhancedDepthFirstSearch = (graph, callback) => { + const vertices = graph.vertices; + const adjList = graph.adjList; + const color = initializeColor(vertices); + const discovery = {}; // Discovery times + const finished = {}; // Finish times + const predecessors = {}; + const time = { count: 0 }; + + for (let i = 0; i < vertices.length; i++) { + finished[vertices[i]] = 0; + discovery[vertices[i]] = 0; + predecessors[vertices[i]] = null; + } + + for (let i = 0; i < vertices.length; i++) { + if (color[vertices[i]] === Colors.WHITE) { + enhancedDFSVisit(vertices[i], color, discovery, finished, predecessors, time, adjList); + } + } + + return { discovery, finished, predecessors}; +} + +const enhancedDFSVisit = (vertex, color, discovery, finished, predecessors, time, adjList) => { + color[vertex] = Colors.GREY; + discovery[vertex] = ++time.count; // Record discovery time + const neighbors = adjList.get(vertex); + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i]; + if (color[neighbor] === Colors.WHITE) { + predecessors[neighbor] = vertex; // Record predecessor + enhancedDFSVisit(neighbor, color, discovery, finished, predecessors, time, adjList); + } + } + color[vertex] = Colors.BLACK; + finished[vertex] = ++time.count; // Record finish time +}; + +module.exports = { depthFirstSearch, enhancedDepthFirstSearch}; \ No newline at end of file diff --git a/src/13-graph/dijkstra.js b/src/13-graph/dijkstra.js new file mode 100644 index 00000000..230a696e --- /dev/null +++ b/src/13-graph/dijkstra.js @@ -0,0 +1,54 @@ +// src/13-graph/dijkstra.js + +const INF = Number.MAX_SAFE_INTEGER; // Infinity + +const dijkstra = (graph, src) => { + const dist = []; + const visited = []; + const pred = []; // Predecessor array + const { length } = graph; + for (let i = 0; i < length; i++) { + dist[i] = INF; + visited[i] = false; + pred[i] = null; // Initialize predecessors + } + dist[src] = 0; + for (let i = 0; i < length - 1; i++) { + const unv = minDistance(dist, visited); + visited[unv] = true; + for (let v = 0; v < length; v++) { + if (!visited[v] && graph[unv][v] !== 0 && dist[unv] !== INF && dist[unv] + graph[unv][v] < dist[v]) { + dist[v] = dist[unv] + graph[unv][v]; + pred[v] = unv; // Update predecessor + } + } + } + + // Construct paths from predecessors + const paths = {}; + for (let i = 0; i < length; i++) { + paths[i] = []; + let crawl = i; + paths[i].push(crawl); + while (pred[crawl] !== null) { + paths[i].push(pred[crawl]); + crawl = pred[crawl]; + } + paths[i].reverse(); + } + return { distances: dist, predecessors: paths }; +}; + +const minDistance = (dist, visited) => { + let min = INF; + let minIndex = -1; + for (let v = 0; v < dist.length; v++) { + if (visited[v] === false && dist[v] <= min) { + min = dist[v]; + minIndex = v; + } + } + return minIndex; +}; + +module.exports = dijkstra; \ No newline at end of file diff --git a/src/13-graph/floyd-warshall.js b/src/13-graph/floyd-warshall.js new file mode 100644 index 00000000..df4fd4db --- /dev/null +++ b/src/13-graph/floyd-warshall.js @@ -0,0 +1,41 @@ +// src/13-graph/floyd-warshall.js + +const INF = Number.MAX_SAFE_INTEGER; // Infinity + +const initializeMatrix = (graph) => { + const dist = []; + const { length } = graph; + for (let i = 0; i < length; i++) { + dist[i] = []; + for (let j = 0; j < length; j++) { + if (i === j) { + dist[i][j] = 0; + } else if (!isFinite(graph[i][j])) { + dist[i][j] = INF; + } else { + dist[i][j] = graph[i][j]; + } + } + } + return dist; +} + +const floydWarshall = (graph) => { + const { length } = graph; + const dist = initializeMatrix(graph); + + // Consider each airport as an intermediate point + for (let k = 0; k < length; k++) { + for (let i = 0; i < length; i++) { + for (let j = 0; j < length; j++) { + // If a shorter path is found through an intermediate airport, update the distance + if (dist[i][k] + dist[k][j] < dist[i][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + return dist; +}; + +module.exports = floydWarshall; \ No newline at end of file diff --git a/src/13-graph/graph.js b/src/13-graph/graph.js new file mode 100644 index 00000000..fcdcf73f --- /dev/null +++ b/src/13-graph/graph.js @@ -0,0 +1,55 @@ +// src/13-graph/graph.js + +// Graph class +class Graph { + + #isDirected = false; + #vertices = []; + #adjList = new Map(); + + constructor(isDirected = false) { + this.#isDirected = isDirected; + } + + addVertex(vertex) { + if (!this.#vertices.includes(vertex)) { + this.#vertices.push(vertex); + this.#adjList.set(vertex, []); + } + } + + addEdge(vertex, edge) { + if (!this.#adjList.get(vertex)) { + this.addVertex(vertex); + } + if (!this.#adjList.get(edge)) { + this.addVertex(edge); + } + this.#adjList.get(vertex).push(edge); + if (!this.#isDirected) { + this.#adjList.get(edge).push(vertex); + } + } + + get vertices() { + return this.#vertices; + } + + get adjList() { + return this.#adjList; + } + + toString() { + let s = ''; + this.#vertices.forEach(vertex => { + s += `${vertex} -> `; + this.#adjList.get(vertex).forEach(neighbor => { + s += `${neighbor} `; + }); + s += '\n'; + }); + return s; + } +} + +module.exports = Graph; \ No newline at end of file diff --git a/src/13-graph/kruskal.js b/src/13-graph/kruskal.js new file mode 100644 index 00000000..e23153e2 --- /dev/null +++ b/src/13-graph/kruskal.js @@ -0,0 +1,69 @@ + +const INF = Number.MAX_SAFE_INTEGER; + +const kruskal = (graph) => { + const { length } = graph; + const parent = []; // Stores the MST + let ne = 0; // Number of edges in the MST + let a; let b; let u; let v; + const cost = initializeCost(graph); // Create a copy of the graph + + // While the MST has fewer edges than the total number of vertices - 1 + while (ne < length - 1) { + for (let i = 0, min = INF; i < length; i++) { + for (let j = 0; j < length; j++) { + // Find the edge with the minimum cost + if (cost[i][j] < min) { + min = cost[i][j]; + a = u = i; + b = v = j; + } + } + } + + u = find(u, parent); // Find the set of vertex u + v = find(v, parent); // Find the set of vertex v + + // If adding the edge doesn't create a cycle, add it to the MST + if (union(u, v, parent)) { + ne++; + } + + cost[a][b] = cost[b][a] = INF; // Remove the edge from the cost matrix + } + + return parent; // Return the MST +}; + +// Helper function to initialize the cost matrix +const initializeCost = (graph) => { + const { length } = graph; + const cost = Array(length).fill(null).map(() => Array(length).fill(null)); + + for (let i = 0; i < length; i++) { + for (let j = 0; j < length; j++) { + cost[i][j] = graph[i][j] || INF; + } + } + + return cost; +}; + +// Helper function to find the set of an element i +const find = (i, parent) => { + while (parent[i]) { + i = parent[i]; + } + return i; +}; + +// Helper function to union two sets of i and j +const union = (i, j, parent) => { + if (i !== j) { + parent[j] = i; + return true; + } + return false; +}; + +module.exports = kruskal; \ No newline at end of file diff --git a/src/13-graph/leetcode/course-schedule.ts b/src/13-graph/leetcode/course-schedule.ts new file mode 100644 index 00000000..ec850f89 --- /dev/null +++ b/src/13-graph/leetcode/course-schedule.ts @@ -0,0 +1,56 @@ +// 207. Course Schedule +// https://leetcode.com/problems/course-schedule/ + +// Time complexity: O(V + E) +// Space complexity: O(V + E) + +function canFinish(numCourses: number, prerequisites: number[][]): boolean { + const adjList = new Map(); + const visited = new Set(); + + // Create adjacency list + for (let i = 0; i < numCourses; i++) { + adjList.set(i, []); + } + for (const [course, pre] of prerequisites) { + adjList.get(pre).push(course); + } + + // DFS function to check for cycles + const dfs = (course, cycle) => { + if (cycle.has(course)) { + return true; + } + if (visited.has(course)) { + return false; + } + cycle.add(course); + visited.add(course); + for (const neighbor of adjList.get(course)) { + if (dfs(neighbor, cycle)) { + return true; + } + } + cycle.delete(course); + return false; + } + + // Check for cycles starting from each course + for (let i = 0; i < numCourses; i++) { + if (dfs(i, new Set())) { + return false; // Cycle found + } + } + + return true; // No cycles found +} + +// Test cases +const numCourses = 2; +const prerequisites = [ + [1, 0], + [0, 1], +]; +console.log(canFinish(numCourses, prerequisites)); // false + +// to see the output of this file use the command: npx ts-node src/13-graph/leetcode/course-schedule.ts \ No newline at end of file diff --git a/src/13-graph/leetcode/number-of-islands.ts b/src/13-graph/leetcode/number-of-islands.ts new file mode 100644 index 00000000..3473d5dd --- /dev/null +++ b/src/13-graph/leetcode/number-of-islands.ts @@ -0,0 +1,52 @@ +// 200. Number of Islands +// https://leetcode.com/problems/number-of-islands/ + +function numIslands(grid: string[][]): number { + let count = 0; + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[0].length; j++) { + if (grid[i][j] === "1") { + count++; + dfs(grid, i, j); + } + } + } + return count; +} + +function dfs(grid: string[][], i: number, j: number) { + if ( + i < 0 || + j < 0 || + i >= grid.length || + j >= grid[0].length || + grid[i][j] === "0" + ) { + return; + } + grid[i][j] = "0"; + dfs(grid, i + 1, j); + dfs(grid, i - 1, j); + dfs(grid, i, j + 1); + dfs(grid, i, j - 1); +} + +console.log( + numIslands([ + ["1", "1", "1", "1", "0"], + ["1", "1", "0", "1", "0"], + ["1", "1", "0", "0", "0"], + ["0", "0", "0", "0", "0"], + ]) +); // 1 + +console.log( + numIslands([ + ["1","1","0","0","0"], + ["1","1","0","0","0"], + ["0","0","1","0","0"], + ["0","0","0","1","1"], + ]) +); // 3 + +// to see the output of this file use the command: npx ts-node src/13-graph/leetcode/number-of-islands.ts \ No newline at end of file diff --git a/src/13-graph/prim.js b/src/13-graph/prim.js new file mode 100644 index 00000000..1b03fa65 --- /dev/null +++ b/src/13-graph/prim.js @@ -0,0 +1,47 @@ +const INF = Number.MAX_SAFE_INTEGER; + +const minKey = (graph, key, visited) => { + let min = INF; + let minIndex = 0; + for (let v = 0; v < graph.length; v++) { + if (visited[v] === false && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + return minIndex; +}; + +const prim = (graph) => { + const parent = []; // Stores the MST + const key = []; // Keeps track of the minimum edge weights + const visited = []; // Marks visited vertices + const { length } = graph; + + // Initialize all key values as infinite and visited as false + for (let i = 0; i < length; i++) { + key[i] = INF; + visited[i] = false; + } + + key[0] = 0; // Start with the first vertex + parent[0] = -1; // The first vertex is the root of the MST + + // Find the MST for all vertices + for (let i = 0; i < length - 1; i++) { + const u = minKey(graph, key, visited); // Select the vertex with the minimum key value + visited[u] = true; // Mark the selected vertex as visited + + // Update key values and parent for adjacent vertices + for (let v = 0; v < length; v++) { + if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) { + parent[v] = u; // Update parent to store the edge in the MST + key[v] = graph[u][v]; // Update key value to the minimum edge weight + } + } + } + + return parent; // Return the MST +}; + +module.exports = prim; \ No newline at end of file From 55af0b569613e8e4e80401a974f9d0289a5ff6ad Mon Sep 17 00:00:00 2001 From: Loiane Date: Sun, 27 Oct 2024 09:50:52 -0400 Subject: [PATCH 43/43] removed unused file --- src/04-stack/__test__/stack-object.test.ts | 97 ---------------------- 1 file changed, 97 deletions(-) delete mode 100644 src/04-stack/__test__/stack-object.test.ts diff --git a/src/04-stack/__test__/stack-object.test.ts b/src/04-stack/__test__/stack-object.test.ts deleted file mode 100644 index 7164881b..00000000 --- a/src/04-stack/__test__/stack-object.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { describe, expect, test, beforeEach } from '@jest/globals'; -import Stack from '../stack-object'; - -describe('Stack', () => { - let stack: Stack; - - beforeEach(() => { - stack = new Stack(); - }); - - test('push should add an item to the stack', () => { - stack.push(1); - stack.push(2); - stack.push(3); - - expect(stack.size).toBe(3); - }); - - test('pop should remove and return the top item from the stack', () => { - stack.push(1); - stack.push(2); - stack.push(3); - - const poppedItem = stack.pop(); - - expect(poppedItem).toBe(3); - expect(stack.size).toBe(2); - }); - - test('pop should return undefined if the stack is empty', () => { - expect(stack.pop()).toBeUndefined(); - }); - - test('peek should return the top item from the stack without removing it', () => { - stack.push(1); - stack.push(2); - stack.push(3); - - const topItem = stack.peek(); - - expect(topItem).toBe(3); - expect(stack.size).toBe(3); - }); - - test('isEmpty should return true if the stack is empty', () => { - expect(stack.isEmpty()).toBe(true); - - stack.push(1); - - expect(stack.isEmpty()).toBe(false); - }); - - test('size should return the number of items in the stack', () => { - expect(stack.size).toBe(0); - - stack.push(1); - stack.push(2); - stack.push(3); - - expect(stack.size).toBe(3); - }); - - test('clear should remove all items from the stack', () => { - stack.push(1); - stack.push(2); - stack.push(3); - - stack.clear(); - - expect(stack.size).toBe(0); - expect(stack.isEmpty()).toBe(true); - }); - - test('toString should return a string representation of the stack', () => { - stack.push(1); - stack.push(2); - stack.push(3); - - expect(stack.toString()).toBe('1, 2, 3'); - }); - - test('toString should return an empty string if the stack is empty', () => { - expect(stack.toString()).toBe('Empty Stack'); - }); - - test('toString should return a string representation of the stack with custom object', () => { - const stack = new Stack<{ key: string; value: number }>(); - - stack.push({ key: 'a', value: 1 }); - stack.push({ key: 'b', value: 2 }); - stack.push({ key: 'c', value: 3 }); - - expect(stack.toString()).toBe( - '{"key":"a","value":1}, {"key":"b","value":2}, {"key":"c","value":3}' - ); - }); -}); \ No newline at end of file