diff --git a/L-A/0010 Design Cancellable Function (L-A)/Design Cancellable Function.js b/L-A/0010 Design Cancellable Function (L-A)/Design Cancellable Function.js new file mode 100644 index 0000000..bfe1952 --- /dev/null +++ b/L-A/0010 Design Cancellable Function (L-A)/Design Cancellable Function.js @@ -0,0 +1,22 @@ +const cancellable = (generator) => { + let cancel; + const cancelPromise = new Promise((_, reject) => { + cancel = () => reject("Cancelled"); + }); + // Every Promise rejection has to be caught. + cancelPromise.catch(() => {}); + + const promise = (async () => { + let next = generator.next(); + while (!next.done) { + try { + next = generator.next(await Promise.race([next.value, cancelPromise])); + } catch (e) { + next = generator.throw(e); + } + } + return next.value; + })(); + + return [cancel, promise]; +}; diff --git a/L-A/0010 Design Cancellable Function (L-A)/README.md b/L-A/0010 Design Cancellable Function (L-A)/README.md new file mode 100644 index 0000000..5d88b31 --- /dev/null +++ b/L-A/0010 Design Cancellable Function (L-A)/README.md @@ -0,0 +1,74 @@ +# 2650. Design Cancellable Function +[LeetCode](https://leetcode.com/problems/design-cancellable-function/) + +Sometimes you have a long running task, and you may wish to cancel it before it completes. To help with this goal, write a function `cancellable` that accepts a generator object and returns an array of two values: a cancel function and a promise. +You may assume the generator function will only yield promises. It is your function's responsibility to pass the values resolved by the promise back to the generator. If the promise rejects, your function should throw that error back to the generator. +If the cancel callback is called before the generator is done, your function should throw an error back to the generator. That error should be the string `"Cancelled"` (Not an `Error` object). If the error was caught, the returned promise should resolve with the next value that was yielded or returned. Otherwise, the promise should reject with the thrown error. No more code should be executed. + +When the generator is done, the promise your function returned should resolve the value the generator returned. If, however, the generator throws an error, the returned promise should reject with the error. + +An example of how your code would be used: +```javascript +function* tasks() { + const val = yield new Promise(resolve => resolve(2 + 2)); + yield new Promise(resolve => setTimeout(resolve, 100)); + return val + 1; // calculation shouldn't be done. +} +const [cancel, promise] = cancellable(tasks()); +setTimeout(cancel, 50); +promise.catch(console.log); // logs "Cancelled" at t=50ms +``` +If instead `cancel()` was not called or was called after `t=100ms`, the promise would have resolved 5. + + +## Example 1: + +Input: +```javascript +generatorFunction = function*() { + return 42; +} +cancelledAt = 100 +``` +**Output:** `{"resolved": 42}`
+**Explanation:** +```javascript +const generator = generatorFunction(); +const [cancel, promise] = cancellable(generator); +setTimeout(cancel, 100); +promise.then(console.log); // resolves 42 at t=0ms +``` +The generator immediately yields 42 and finishes. Because of that, the returned promise immediately resolves 42. Note that cancelling a finished generator does nothing. + +## Example 2: + +Input: +```javascript +generatorFunction = function*() { + const msg = yield new Promise(res => res("Hello")); + throw `Error: ${msg}`; +} +cancelledAt = null +``` +**Output:** `{"rejected": "Error: Hello"}`
+**Explanation:** +A promise is yielded. The function handles this by waiting for it to resolve and then passes the resolved value back to the generator. Then an error is thrown which has the effect of causing the promise to reject with the same thrown error. + +## Example 3: + +Input: +```javascript +generatorFunction = function*() { + yield new Promise(res => setTimeout(res, 200)); + return "Success"; +} +cancelledAt = 100 +``` +**Output:** `{"rejected": "Cancelled"}`
+**Explanation:** +While the function is waiting for the yielded promise to resolve, cancel() is called. This causes an error message to be sent back to the generator. Since this error is uncaught, the returned promise rejected with this error. + +## Constraints: + +`cancelledAt == null or 0 <= cancelledAt <= 1000`
+`generatorFunction` returns a generator object diff --git a/L-A/0011 The Fiscal Code/README.md b/L-A/0011 The Fiscal Code/README.md new file mode 100644 index 0000000..3465b45 --- /dev/null +++ b/L-A/0011 The Fiscal Code/README.md @@ -0,0 +1,57 @@ +# The Fiscal Code +**[Edabit Problem](https://edabit.com/challenge/Pa2rHJ6KeRBTF28Pg)** + +Each person in Italy has an unique identifying ID code issued by the national tax office after the birth registration: the Fiscal Code ([Codice Fiscale](https://en.wikipedia.org/wiki/Italian_fiscal_code_card)). + +Given an object containing the personal data of a person (name, surname, gender and date of birth) return the 11 code characters as a string following these steps: + +- Generate 3 capital letters from the surname, if it has: + + - At least 3 consonants then the first three consonants are used. (Newman -> NWM). + - Less than 3 consonants then vowels will replace missing characters in the same order they appear (Fox -> FXO | Hope -> HPO). + - Less than three letters then "X" will take the third slot after the consonant and the vowel (Yu -> YUX). + +- Generate 3 capital letters from the name, if it has: + + - Exactly 3 consonants then consonants are used in the order they appear (Matt -> MTT). + - More than 3 consonants then first, third and fourth consonant are used (Samantha -> SNT | Thomas -> TMS). + - Less than 3 consonants then vowels will replace missing characters in the same order they appear (Bob -> BBO | Paula -> PLA). + - Less than three letters then "X" will take the the third slot after the consonant and the vowel (Al -> LAX). + +- Generate 2 numbers, 1 letter and 2 numbers from date of birth and gender: + + - Take the last two digits of the year of birth (1985 -> 85). + - Generate a letter corresponding to the month of birth (January -> A | December -> T) using the table for conversion included in the code. + - For males take the day of birth adding one zero at the start if is less than 10 (any 9th day -> 09 | any 20th day -> 20). + - For females take the day of birth and sum 40 to it (any 9th day -> 49 | any 20th day -> 60). + +### Examples: + +```javascipt +fiscalCode({ + name: "Matt", + surname: "Edabit", + gender: "M", + dob: "1/1/1900" +}) ➞ "DBTMTT00A01" + +fiscalCode({ + name: "Helen", + surname: "Yu", + gender: "F", + dob: "1/12/1950" +}) ➞ "YUXHLN50T41" + +fiscalCode({ + name: "Mickey", + surname: "Mouse", + gender: "M", + dob: "16/1/1928" +}) ➞ "MSOMKY28A16" +``` + +**Notes:** +- Code letters must be uppercase. +- Date of birth is given in D/M/YYYY format. +- The conversion table for months is already in the starting code. +- Y is not a vowel. diff --git a/L-A/0011 The Fiscal Code/TheFiscalCode.js b/L-A/0011 The Fiscal Code/TheFiscalCode.js new file mode 100644 index 0000000..f1f8a2d --- /dev/null +++ b/L-A/0011 The Fiscal Code/TheFiscalCode.js @@ -0,0 +1,79 @@ +function fiscalCode(data) { + const monthsConversion = { + '01': 'A', '02': 'B', '03': 'C', '04': 'D', '05': 'E', '06': 'H', + '07': 'L', '08': 'M', '09': 'P', '10': 'R', '11': 'S', '12': 'T' + }; + + // Helper function to generate code for names and surnames + function generateCode(name, isSurname) { + const vowels = 'AEIOU'; + let consonants = ''; + let code = ''; + + // Helper function to check if a character is a consonant + function isConsonant(char) { + return /[BCDFGHJKLMNPQRSTVWXYZ]/.test(char); + } + + for (let i = 0; i < name.length && consonants.length < 3; i++) { + const char = name[i].toUpperCase(); + if (isConsonant(char)) { + consonants += char; + } + } + + if (consonants.length < 3) { + for (let i = 0; i < name.length && consonants.length < 3; i++) { + const char = name[i].toUpperCase(); + if (vowels.includes(char)) { + consonants += char; + } + } + } + + if (consonants.length < 3) { + consonants += 'X'.repeat(3 - consonants.length); + } + + code = consonants; + + if (isSurname) { + code += 'XXX'; + } else { + code += name.length >= 3 ? name[0] + name[2] + name[3] : name + 'XX'; + } + + return code; + } + + const surnameCode = generateCode(data.surname, true); + const nameCode = generateCode(data.name, false); + + const year = data.dob.split('/')[2].slice(-2); + const month = monthsConversion[data.dob.split('/')[1]]; + const day = (data.gender === 'F' ? 40 + parseInt(data.dob.split('/')[0]) : parseInt(data.dob.split('/')[0])).toString().padStart(2, '0'); + + return `${surnameCode}${nameCode}${year}${month}${day}`; +} + +// Examples +console.log(fiscalCode({ + name: "Matt", + surname: "Edabit", + gender: "M", + dob: "1/1/1900" +})); // ➞ "DBTMTT00A01" + +console.log(fiscalCode({ + name: "Helen", + surname: "Yu", + gender: "F", + dob: "1/12/1950" +})); // ➞ "YUXHLN50T41" + +console.log(fiscalCode({ + name: "Mickey", + surname: "Mouse", + gender: "M", + dob: "16/1/1928" +})); // ➞ "MSOMKY28A16" diff --git a/L-A/0012 SVG Path Data Parser/README.md b/L-A/0012 SVG Path Data Parser/README.md new file mode 100644 index 0000000..24c40f4 --- /dev/null +++ b/L-A/0012 SVG Path Data Parser/README.md @@ -0,0 +1,54 @@ +# SVG Path Data Parser +[Edabit Problem](https://edabit.com/challenge/ysMrKPGby3FXiYtQn) + +A `` element can usually be found inside an `` element and has an attribute **d** that represents the definition of the outline of a shape. + +A brief summary about this attribute: + +It contains commands (letters) and coordinates (numbers) +All instructions are expressed as one character (e.g., a moveto is expressed as an **M**). +Superfluous white space and separators such as commas can be eliminated (e.g., M 10 10 L 20 20 contains unnecessary spaces and could be expressed more compactly as `M10 10L20 20`). +The command letter can be eliminated on subsequent commands if the same command is used multiple times in a row (e.g., you can drop the second L in `M 10 20 L 20 10 L -10 -20` and use `M 10 20 L 20 10 -10 -20` instead). + +Your job is to build a parser that will convert this string in an array of commands, where each array element is an object with the `command` letter and an array of `parameters`. + +This summary is incomplete but should get you started, for more information please refer to the W3C specification found in the resources tab. + +## Examples + +```javascript +pathDataParser("") ➞ [] + +pathDataParser("M 0 0") ➞ [{command: 'M', parameters: [0, 0]}] + +pathDataParser("M 1 1.5 L 0 1.5 0 0.5 1 0.5 0.5 0 0 0.5 1 1.5 1 0.5 0 1.5" ➞ [ + {command: "M", parameters: [1, 1.5]}, + {command: "L", parameters: [0, 1.5, 0, 0.5, 1, 0.5, 0.5, 0, 0, 0.5, 1, 1.5, 1, 0.5, 0, 1.5]} +] + +pathDataParser("M 0,1 h 1 v -1 h 1 v 1 h 1 C 2,1 3,3 1.5,3 C 0,3 1,1 0,1 z" ➞ [ + {command: "M", parameters: [0, 1]}, + {command: "h", parameters: [1]}, + {command: "v", parameters: [-1]}, + {command: "h", parameters: [1]}, + {command: "v", parameters: [1]}, + {command: "h", parameters: [1]}, + {command: "C", parameters: [2, 1, 3, 3, 1.5, 3]}, + {command: "C", parameters: [0, 3, 1, 1, 0, 1]}, + {command: "z", parameters: []} +] +``` + +## Notes: + +- Return an empty array if no commands are found (example #1) +- The z (or Z) command is the only one without parameters, in this case return an empty array (see last command of example #4) +- The parameters array contains numbers, not strings, so you'll have to convert them +- Sometimes numbers can be very compressed to save space, let's look at some examples that might trip you up: + - Decimal numbers can start with a dot: .4 is the same as 0.4 + - If a negative number comes after another number, the space is optional: 0-4 is equal to 0 -4 + - Multiple decimal numbers in a row can be very tricky, remember that a number CAN NOT have more than 1 dot, so this: 1.2.34 is actually 2 different numbers: 1.2 and 0.34 +- Some examples have commas, some do not, some have multiline strings, some are a single line, remember to take into account all valid cases! Check out the tests tab to find out more. + +## By: arindal1 +***[GitHub](https://github.com/arindal1)*** diff --git a/L-A/0012 SVG Path Data Parser/SVGDataParser.js b/L-A/0012 SVG Path Data Parser/SVGDataParser.js new file mode 100644 index 0000000..931a790 --- /dev/null +++ b/L-A/0012 SVG Path Data Parser/SVGDataParser.js @@ -0,0 +1,40 @@ +function pathDataParser(data) { + const commands = []; + let currentCommand = null; + let currentParams = []; + let currentParam = ''; + + for (let i = 0; i < data.length; i++) { + const char = data[i]; + + if (char === ' ' || char === ',') { + if (currentParam !== '') { + currentParams.push(parseFloat(currentParam)); + currentParam = ''; + } + } else if (/[a-zA-Z]/.test(char)) { + if (currentCommand !== null) { + commands.push({ command: currentCommand, parameters: currentParams }); + currentParams = []; + } + currentCommand = char; + } else { + currentParam += char; + } + } + + if (currentCommand !== null) { + if (currentParam !== '') { + currentParams.push(parseFloat(currentParam)); + } + commands.push({ command: currentCommand, parameters: currentParams }); + } + + return commands; +} + +// Examples +console.log(pathDataParser("")); +console.log(pathDataParser("M 0 0")); +console.log(pathDataParser("M 1 1.5 L 0 1.5 0 0.5 1 0.5 0.5 0 0 0.5 1 1.5 1 0.5 0 1.5")); +console.log(pathDataParser("M 0,1 h 1 v -1 h 1 v 1 h 1 C 2,1 3,3 1.5,3 C 0,3 1,1 0,1 z")); diff --git a/L-A/0013 Caesar's Cipher/CaesarsCipher.js b/L-A/0013 Caesar's Cipher/CaesarsCipher.js new file mode 100644 index 0000000..0c46e12 --- /dev/null +++ b/L-A/0013 Caesar's Cipher/CaesarsCipher.js @@ -0,0 +1,31 @@ +function caesarCipher(str, k) { + // Helper function to shift a single character by k positions + function shiftChar(char, k) { + const isUpperCase = /[A-Z]/.test(char); + const alphabetSize = 26; + + const baseCharCode = isUpperCase ? 'A'.charCodeAt(0) : 'a'.charCodeAt(0); + const shiftedCharCode = ((char.charCodeAt(0) - baseCharCode + k) % alphabetSize + alphabetSize) % alphabetSize + baseCharCode; + + return String.fromCharCode(shiftedCharCode); + } + + let result = ''; + + for (let i = 0; i < str.length; i++) { + const char = str[i]; + + if (/[A-Za-z]/.test(char)) { + result += shiftChar(char, k); + } else { + result += char; // Non-alphabetic characters remain unchanged + } + } + + return result; +} + +// Examples +console.log(caesarCipher("middle-Outz", 2)); // ➞ "okffng-Qwvb" +console.log(caesarCipher("Always-Look-on-the-Bright-Side-of-Life", 5)); // ➞ "Fqbfdx-Qttp-ts-ymj-Gwnlmy-Xnij-tk-Qnkj" +console.log(caesarCipher("A friend in need is a friend indeed", 20)); // ➞ "U zlcyhx ch hyyx cm u zlcyhx chxyyx" diff --git a/L-A/0013 Caesar's Cipher/README.md b/L-A/0013 Caesar's Cipher/README.md new file mode 100644 index 0000000..ef84879 --- /dev/null +++ b/L-A/0013 Caesar's Cipher/README.md @@ -0,0 +1,34 @@ +# Caesar's Cipher +[Edabit Problem](https://edabit.com/challenge/a33jdGXkaQRtK9ZTs) + +Julius Caesar protected his confidential information by encrypting it using a cipher. Caesar's cipher (check ***Resources*** tab for more info) shifts each letter by a number of letters. If the shift takes you past the end of the alphabet, just rotate back to the front of the alphabet. In the case of a rotation by *3, w, x, y* and *z* would map to *z, a, b* and *c*. + +Create a function that takes a string *s* (text to be encrypted) and an integer *k* (the rotation factor). It should return an encrypted string. + +## Example: +```javascript +caesarCipher("middle-Outz", 2) ➞ "okffng-Qwvb" + +// m -> o +// i -> k +// d -> f +// d -> f +// l -> n +// e -> g +// - - +// O -> Q +// u -> w +// t -> v +// z -> b + +caesarCipher("Always-Look-on-the-Bright-Side-of-Life", 5) +➞ "Fqbfdx-Qttp-ts-ymj-Gwnlmy-Xnij-tk-Qnkj" + +caesarCipher("A friend in need is a friend indeed", 20) +➞ "U zlcyhx ch hyyx cm u zlcyhx chxyyx" +``` + +### Notes: All test input will be a valid ASCII string. + +## By: arindal1 +***[GitHub](https://github.com/arindal1)*** diff --git a/L-A/0014 Memoize II/MemoizeII.js b/L-A/0014 Memoize II/MemoizeII.js new file mode 100644 index 0000000..3d7f3c8 --- /dev/null +++ b/L-A/0014 Memoize II/MemoizeII.js @@ -0,0 +1,26 @@ + +const RES = Symbol("result"); + +/** + * @param {Function} fn + */ +function memoize(fn) { + const globalCache = new Map(); + + return (...params) => { + let currentCache = globalCache; + for(const param of params) { + if (!currentCache.has(param)) { + currentCache.set(param, new Map()); + } + currentCache = currentCache.get(param); + } + + if (currentCache.has(RES)) return currentCache.get(RES); + + const result = fn(...params); + + currentCache.set(RES, result); + return result; + } +} diff --git a/L-A/0014 Memoize II/README.md b/L-A/0014 Memoize II/README.md new file mode 100644 index 0000000..5e046c3 --- /dev/null +++ b/L-A/0014 Memoize II/README.md @@ -0,0 +1,80 @@ +# Memoize II +[LeetCode Problem]https://leetcode.com/problems/memoize-ii/ + +Given a function fn, return a memoized version of that function. + +A memoized function is a function that will never be called twice with the same inputs. Instead it will return a cached value. + +fn can be any function and there are no constraints on what type of values it accepts. Inputs are considered identical if they are === to each other. + +## Example 1: +```javascript +Input: +getInputs = () => [[2,2],[2,2],[1,2]] +fn = function (a, b) { return a + b; } +Output: [{"val":4,"calls":1},{"val":4,"calls":1},{"val":3,"calls":2}] +Explanation: +const inputs = getInputs(); +const memoized = memoize(fn); +for (const arr of inputs) { + memoized(...arr); +} + +For the inputs of (2, 2): 2 + 2 = 4, and it required a call to fn(). +For the inputs of (2, 2): 2 + 2 = 4, but those inputs were seen before so no call to fn() was required. +For the inputs of (1, 2): 1 + 2 = 3, and it required another call to fn() for a total of 2. +``` + +## Example 2: +```javascript +Input: +getInputs = () => [[{},{}],[{},{}],[{},{}]] +fn = function (a, b) { return ({...a, ...b}); } +Output: [{"val":{},"calls":1},{"val":{},"calls":2},{"val":{},"calls":3}] +Explanation: +Merging two empty objects will always result in an empty object. It may seem like there should only be 1 call to fn() because of cache-hits, however none of those objects are === to each other. +``` + + +## Example 3: +```javascript +Input: +getInputs = () => { const o = {}; return [[o,o],[o,o],[o,o]]; } +fn = function (a, b) { return ({...a, ...b}); } +Output: [{"val":{},"calls":1},{"val":{},"calls":1},{"val":{},"calls":1}] +Explanation: +Merging two empty objects will always result in an empty object. The 2nd and 3rd third function calls result in a cache-hit. This is because every object passed in is identical. +``` + +### Constraints: +```javascript +1 <= inputs.length <= 105 +0 <= inputs.flat().length <= 105 +inputs[i][j] != NaN +``` + +## Approach: +Map is perfect for storing single argument. Each consecutive argument must have cache based on previous argument, which will form a tree like structure, like this: +```javascript +fn(1,3,4) // { 1: { 3: { 4: {}}}}; +fn(1,4,3) // { 1: { 3: { 4: {}}, 4: { 3: {}}}}; +``` +The only problem, is how do we store our result. It's been never stated, that memoized function will be called with the same amount of arguments every time. We need a way to store our result anywhere on the path and also make sure that it will never be misteaken with argument. Here is example. +```javascript +fn(1,3) // { 1: { 3: 4}}; +-> 4 +fn(1) // { 1: { 3: 4}}; +-> { 3: 4 } // returning cache branch as result +``` +There are different aproaches to overcome this. But since every Symbol() call is guaranteed to return a unique Symbol we can use it to store actual result with guarantee, that it will never match any argument. + +## Problem Added By + +- [deveshidwivedi](https://github.com/deveshidwivedi) + + + +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-A/0015 Largest Prime Palindrome/LargestPrimePalindrome.js b/L-A/0015 Largest Prime Palindrome/LargestPrimePalindrome.js new file mode 100644 index 0000000..e7e2df6 --- /dev/null +++ b/L-A/0015 Largest Prime Palindrome/LargestPrimePalindrome.js @@ -0,0 +1,39 @@ +function isPrime(num) { + if (num <= 1) { + return false; + } + if (num <= 3) { + return true; + } + if (num % 2 === 0 || num % 3 === 0) { + return false; + } + let i = 5; + while (i * i <= num) { + if (num % i === 0 || num % (i + 2) === 0) { + return false; + } + i += 6; + } + return true; +} + +function isPalindrome(num) { + const numStr = num.toString(); + const reversedStr = numStr.split("").reverse().join(""); + return numStr === reversedStr; +} + +function largestPrimePalindrome(n) { + for (let i = n; i >= 2; i--) { + if (isPrime(i) && isPalindrome(i)) { + return i; + } + } + return null; // No prime palindrome found in the range +} + +// Test cases +console.log(largestPrimePalindrome(100)); // 7 +console.log(largestPrimePalindrome(50)); // 7 +console.log(largestPrimePalindrome(10)); // 7 diff --git a/L-A/0015 Largest Prime Palindrome/README.md b/L-A/0015 Largest Prime Palindrome/README.md new file mode 100644 index 0000000..3617356 --- /dev/null +++ b/L-A/0015 Largest Prime Palindrome/README.md @@ -0,0 +1,81 @@ +# 0015 Largest Prime Palindrome ( L-A ) + +## Problem + +Write a JavaScript function called largestPrimePalindrome that takes an integer **n** as input and returns the largest prime palindrome that is less than or equal to **n**. A prime palindrome is a number that is both prime and reads the same forward and backward. + +**Example** + +``` +If the input n is 100, the function should return 7 because the largest prime palindrome less than or equal to 100 is 7. + +If the input n is 50, the function should return 7 because 7 is the largest prime palindrome less than or equal to 50. + +If the input n is 10, the function should return 7 because 7 is the largest prime palindrome less than or equal to 10. +``` + +## Solutions + +```javascript +function isPrime(num) { + if (num <= 1) { + return false; + } + if (num <= 3) { + return true; + } + if (num % 2 === 0 || num % 3 === 0) { + return false; + } + let i = 5; + while (i * i <= num) { + if (num % i === 0 || num % (i + 2) === 0) { + return false; + } + i += 6; + } + return true; +} + +function isPalindrome(num) { + const numStr = num.toString(); + const reversedStr = numStr.split("").reverse().join(""); + return numStr === reversedStr; +} + +function largestPrimePalindrome(n) { + for (let i = n; i >= 2; i--) { + if (isPrime(i) && isPalindrome(i)) { + return i; + } + } + return null; // No prime palindrome found in the range +} + +// Test cases +console.log(largestPrimePalindrome(100)); // 7 +console.log(largestPrimePalindrome(50)); // 7 +console.log(largestPrimePalindrome(10)); // 7 +``` + +## How it works + +1. The isPrime function checks whether a given number is prime using a primality test that efficiently eliminates multiples of 2 and 3 and then checks for divisibility by numbers of the form 6k ± 1. + +2. The isPalindrome function checks whether a given number is a palindrome by converting it to a string, reversing the string, and comparing it to the original string. + +3. The largestPrimePalindrome function starts from the given number n and counts down to 2, checking each number in reverse order. It uses the isPrime and isPalindrome functions to find the largest prime palindrome in the given range. + +## References + +- [ChatGPT](https://chat.openai.com/) + +## Problem Added By + +- [Tipchan](https://github.com/tsongtheng) + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-A/0015 Longest Valid Parentheses/LongestValidParentheses.js b/L-A/0015 Longest Valid Parentheses/LongestValidParentheses.js new file mode 100644 index 0000000..b19384a --- /dev/null +++ b/L-A/0015 Longest Valid Parentheses/LongestValidParentheses.js @@ -0,0 +1,8 @@ +var longestValidParentheses = function(S) { + let stack = [-1], ans = 0 + for (let i = 0; i < S.length; i++) + if (S[i] === '(') stack.push(i) + else if (stack.length === 1) stack[0] = i + else stack.pop(), ans = Math.max(ans, i - stack[stack.length-1]) + return ans +}; \ No newline at end of file diff --git a/L-A/0015 Longest Valid Parentheses/README.md b/L-A/0015 Longest Valid Parentheses/README.md new file mode 100644 index 0000000..60a6ddb --- /dev/null +++ b/L-A/0015 Longest Valid Parentheses/README.md @@ -0,0 +1,60 @@ +# Longest Valid Parentheses +[LeetCode Problem]https://leetcode.com/problems/longest-valid-parentheses/description/ + +Given a string containing just the characters '(' and ')', return the length of the longest valid (well-formed) parentheses substring. + +## Example 1 +``` +Input: s = "(()" +Output: 2 +Explanation: The longest valid parentheses substring is "()". +``` + +## Example 2: +``` +Input: s = ")()())" +Output: 4 +Explanation: The longest valid parentheses substring is "()()". +``` + +## Example 3: +``` +Input: s = "" +Output: 0 +``` + +### Constraints: +```javascript +0 <= s.length <= 3 * 104 +s[i] is '(', or ')' +``` + +## Approach: +One of the key things to realize about valid parentheses strings is that they're entirely self-satisfied, meaning that while you can have one substring that is entirely inside another, you can't have two substrings that only partially overlap. + +This means that we can use a greedy O(N) time complexity solution to this problem without the need for any kind of backtracking. In fact, we should be able to use a very standard stack-based valid parentheses string algorithm with just three very minor modifications. + +In a stadard valid parentheses string algorithm, we iterate through the string (S) and push the index (i) of any '(' to our stack. Whenever we find a ')', we match it with the last entry on the stack and pop said entry off. We know the string is not valid if we find a ')' while there are no '(' indexes in the stack with which to match it, and also if we have leftover '(' in the stack when we reach the end of S. + +For this problem, we will need to add in a step that updates our answer (ans) when we close a parentheses pair. Since we stored the index of the '(' in our stack, we can easily find the difference between the ')' at i and the last entry in the stack, which should be the length of the valid substring which was just closed. + +But here we run into a problem, because consecutive valid substrings can be grouped into a larger valid substring (ie, '()()' = 4). So instead of counting from the last stack entry, we should actually count from the second to last entry, to include any other valid closed substrings since the most recent '(' that will still remain after we pop the just-matched last stack entry off. + +This, of course, brings us to the second and third changes. Since we're checking the second to last stack entry, what happens in the case of '()()' when you close the second valid substring yet there's only the one stack entry left at the time? + +To avoid this issue, we can just wrap the entire string in another imaginary set of parentheses by starting with stack = [-1], indicating that there's an imaginary '(' just before the beginning of the string at i = 0. + +The other issue is that we will want to continue even if the string up to i becomes invalid due to a ')' appearing when the stack is "empty", or in this case has only our imaginary index left. In that case, we can just effectively restart our stack by updating our imaginary '(' index (stack[0] = i) and continue on. + +Then, once we reach the end of S, we can just return ans. + +## Problem Added By + +- [himanshukoshti](https://github.com/himanshukoshti) + + + +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-B/0025 Seven Boom!/README.md b/L-B/0025 Seven Boom!/README.md new file mode 100644 index 0000000..694f8f1 --- /dev/null +++ b/L-B/0025 Seven Boom!/README.md @@ -0,0 +1,17 @@ +# Seven Boom! +[Edabit Problem](https://edabit.com/challenge/6R6gReGTGwzpwuffD) + +Create a function that takes an array of numbers and return `"Boom!"` if the digit 7 appears in the array. Otherwise, return `"there is no 7 in the array"`. + +Examples: +```javascript +sevenBoom([1, 2, 3, 4, 5, 6, 7]) ➞ "Boom!" +// 7 contains the number seven. + +sevenBoom([8, 6, 33, 100]) ➞ "there is no 7 in the array" +// None of the items contain 7 within them. + +sevenBoom([2, 55, 60, 97, 86]) ➞ "Boom!" +// 97 contains the number seven. +``` + diff --git a/L-B/0025 Seven Boom!/SevenBoom.js b/L-B/0025 Seven Boom!/SevenBoom.js new file mode 100644 index 0000000..efa2f28 --- /dev/null +++ b/L-B/0025 Seven Boom!/SevenBoom.js @@ -0,0 +1,8 @@ +function sevenBoom(arr) { + for (let i = 0; i < arr.length; i++) { + if (String(arr[i]).includes('7')) { + return 'Boom!'; + } + } + return 'there is no 7 in the array'; +} diff --git a/L-B/0026 Calculate Tip/CalculateTip.js b/L-B/0026 Calculate Tip/CalculateTip.js new file mode 100644 index 0000000..1443820 --- /dev/null +++ b/L-B/0026 Calculate Tip/CalculateTip.js @@ -0,0 +1,9 @@ +function calculateTip(totalCost, tipPercentage) { + // Calculate the tip amount by multiplying totalCost by tipPercentage divided by 100 + const tipAmount = (totalCost * tipPercentage) / 100; + return tipAmount; +} + +// Test cases +console.log(calculateTip(50, 15)); // 7.5 +console.log(calculateTip(75, 20)); // 15 diff --git a/L-B/0026 Calculate Tip/README.md b/L-B/0026 Calculate Tip/README.md new file mode 100644 index 0000000..45af127 --- /dev/null +++ b/L-B/0026 Calculate Tip/README.md @@ -0,0 +1,56 @@ +# 0026 Calculate Tip ( L-B ) + +## Problem + +You are at a restaurant, and you want to calculate the tip for your meal. Write a JavaScript function called **calculateTip** that takes two inputs: the total cost of the meal and the tip percentage.Your function should calculate the tip amount and return it. + +**Example** + +``` +If the total cost is $50 and the tip percentage is 15%, the function should return $7.5 because 15% of $50 is $7.5. + +If the total cost is $75 and the tip percentage is 20%, the function should return $15 because 20% of $75 is $15. +``` + +- **_You can assume that the tip percentage is provided as a whole number (e.g., 15 for 15%) and that the total cost is a positive number._** + +**Here's an example of how the function should work:** + +```javascript +console.log(calculateTip(50, 15)); // 7.5 +console.log(calculateTip(75, 20)); // 15 +``` + +## Solutions + +```javascript +function calculateTip(totalCost, tipPercentage) { + // Calculate the tip amount by multiplying totalCost by tipPercentage divided by 100 + const tipAmount = (totalCost * tipPercentage) / 100; + return tipAmount; +} + +// Test cases +console.log(calculateTip(50, 15)); // 7.5 +console.log(calculateTip(75, 20)); // 15 +``` + +## How it works + +1. We define a function calculateTip that takes two parameters: totalCost (the total cost of the meal) and tipPercentage (the percentage of the tip to be calculated). + +2. Inside the function, we calculate the tip amount by multiplying totalCost by tipPercentage divided by 100. This is because tip percentages are usually provided as whole numbers (e.g., 15 for 15%). + +## References + +- [ChatGPT](https://chat.openai.com/) + +## Problem Added By + +- [Tipchan](https://github.com/tsongtheng) + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-B/0026 FileExtension/FileExtension.js b/L-B/0026 FileExtension/FileExtension.js new file mode 100644 index 0000000..6987c7f --- /dev/null +++ b/L-B/0026 FileExtension/FileExtension.js @@ -0,0 +1,17 @@ +function extractFileExtension(fileName) { + // Split the file name into an array of strings at the dot character. + const splitFileName = fileName.split('.'); + + // Check if the split file name array has more than one element. + if (splitFileName.length > 1) { + // Get the last element in the array, which is the file extension. + const fileExtension = splitFileName.pop(); + + // Return the file extension. + return fileExtension; + } + // The file name does not have an extension, so return an empty string. + return ''; +} + +module.exports = extractFileExtension; diff --git a/L-B/0026 FileExtension/FileExtension.test.js b/L-B/0026 FileExtension/FileExtension.test.js new file mode 100644 index 0000000..86bdb7f --- /dev/null +++ b/L-B/0026 FileExtension/FileExtension.test.js @@ -0,0 +1,40 @@ + +const extractFileExtension = require('./FileExtension') + +describe('extractFileExtension', () => { + it('should return the file extension when given a valid file name', () => { + expect(extractFileExtension('example.txt')).toBe('txt'); + expect(extractFileExtension('document.docx')).toBe('docx'); + expect(extractFileExtension('image.jpg')).toBe('jpg'); + }); + + it('should return an empty string when given a file name without an extension', () => { + expect(extractFileExtension('README')).toBe(''); + expect(extractFileExtension('LICENSE')).toBe(''); + }); + + it('should return an empty string when given an empty string', () => { + expect(extractFileExtension('')).toBe(''); + }); +}); + +/* +test output: + PASS ./FileExtension.test.js + extractFileExtension + √ should return the file extension when given a valid file name (4 ms) + √ should return an empty string when given a file name without an extension (1 ms) + √ should return an empty string when given an empty string + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total +Snapshots: 0 total +Time: 0.958 s, estimated 1 s +Ran all test suites. +*/ + +/* +Testing with Jest global installation: +> npm install -g jest +> jest +*/ diff --git a/L-B/0026 FileExtension/README.md b/L-B/0026 FileExtension/README.md new file mode 100644 index 0000000..d69f24a --- /dev/null +++ b/L-B/0026 FileExtension/README.md @@ -0,0 +1,46 @@ +# Get the file extension + +## Problem + +Write a function that takes a string and returns the file extension, which is the string after the last dot (.) in the string. If there is no dot in the string, return empty string. + +## Solution + +```javascript +function extractFileExtension(fileName) { + // Split the file name into an array of strings at the dot character. + const splitFileName = fileName.split("."); + + // Check if the split file name array has more than one element. + if (splitFileName.length > 1) { + // Get the last element in the array, which is the file extension. + const fileExtension = splitFileName.pop(); + + // Return the file extension. + return fileExtension; + } + // The file name does not have an extension, so return an empty string. + return ""; +} +``` + +## How it works + +- The `extractFileExtension` function takes a string as an argument. +- The `split` method is used to split the string into an array of strings at the dot character. +- The `pop` method is used to get the last element in the array, which is the file extension. +- The file extension is returned. + +## References + +- [String.prototype.split()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split) + +## Problem Added By + +- [GitHub](https://github.com/aladin002dz) + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-B/0026 FileExtension/jest.config.json b/L-B/0026 FileExtension/jest.config.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/L-B/0026 FileExtension/jest.config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/L-B/0027 Remove duplicates/README.md b/L-B/0027 Remove duplicates/README.md new file mode 100644 index 0000000..5e79830 --- /dev/null +++ b/L-B/0027 Remove duplicates/README.md @@ -0,0 +1,62 @@ +# 0027 removeDuplicates ( L-B ) + +## Problem +Write a function that removes duplicate values from the given input. +- If the input is an array, return a new array with unique values. +- If the input is a string, return a new string with duplicate characters removed. +- The original order must be preserved. + +## Solution +```js +function removeDuplicates(input) { + let unique = []; + for (let i = 0; i < input.length; i++) { + if (!unique.includes(input[i])) { + unique.push(input[i]); + } + } + if(typeof(input) === "string"){ + unique = unique.toString().replace(/,/g, ''); + } + console.log(unique); + return unique; +} +``` + +## Test Cases +```js +removeDuplicates([2, 4, 8, 4, 2, 6, 9, 2, 6, 8, 10]); +// Output: [2, 4, 8, 6, 9, 10] + +removeDuplicates([1, 1, 1, 4, 0, 6, -2, 2, 6, 7, 10]); +// Output: [1, 4, 0, 6, -2, 2, 7, 10] + +removeDuplicates("zoom"); +// Output: "zom" + +removeDuplicates("hello world"); +// Output: "helo wrd" +``` + +## How it works +- Create an empty array called unique +- Loop through each character or element in the input +- Check if the current value already exists in unique +- If it does not exist, we add it to unique +- After the loop: +- If the input is a string, we convert the array into a string +- Remove commas using replace +- Finally, return the result + +## References +- [MDN – Array.prototype.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) +- [MDN – typeof operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof) + + +## Problem Added By +- [GitHub](https://github.com/rimsha-shoukat) + +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-B/0027 Remove duplicates/removeDuplicates.js b/L-B/0027 Remove duplicates/removeDuplicates.js new file mode 100644 index 0000000..29f6dba --- /dev/null +++ b/L-B/0027 Remove duplicates/removeDuplicates.js @@ -0,0 +1,18 @@ +function removeDuplicates(input) { + let unique = []; + for (let i = 0; i < input.length; i++) { + if (!unique.includes(input[i])) { + unique.push(input[i]); + } + } + if(typeof(input) === "string"){ + unique = unique.toString().replace(/,/g, ''); + } + console.log(unique); + return unique; +} + +removeDuplicates([2, 4, 8, 4, 2, 6, 9, 2, 6, 8, 10]); +removeDuplicates([1, 1, 1, 4, 0, 6, -2, 2, 6, 7, 10]); +removeDuplicates("zoom"); +removeDuplicates("hello world"); \ No newline at end of file diff --git a/L-I/0011 DNA_Matching/DNA_Matching.js b/L-I/0011 DNA_Matching/DNA_Matching.js new file mode 100644 index 0000000..9c511b3 --- /dev/null +++ b/L-I/0011 DNA_Matching/DNA_Matching.js @@ -0,0 +1,22 @@ +function dnaMatch(dna1, dna2) { + // Check if the lengths of the two DNA sequences are different + if (dna1.length !== dna2.length) { + return false; // Different lengths, not a match + } + + // Iterate through each character of the DNA sequences + for (let i = 0; i < dna1.length; i++) { + // Compare characters at the current position + if (dna1[i] !== dna2[i]) { + return false; // Characters don't match, not a match + } + } + + // If we reach this point, all characters match, so it's a match + return true; +} + +// Test cases +console.log(dnaMatch("ACGT", "ACGT")); // true +console.log(dnaMatch("ACGT", "ACTG")); // false +console.log(dnaMatch("AACT", "AACC")); // false diff --git a/L-I/0011 DNA_Matching/README.md b/L-I/0011 DNA_Matching/README.md new file mode 100644 index 0000000..c125365 --- /dev/null +++ b/L-I/0011 DNA_Matching/README.md @@ -0,0 +1,78 @@ +# 0011 DNA_Matching ( L-I ) + +## Problem + +You are given two DNA sequences as strings, representing the genetic code of two individuals. Each DNA sequence consists of a series of characters, where each character can be one of four options: 'A' (adenine), 'C' (cytosine), 'G' (guanine), or 'T' (thymine). + +Write a JavaScript function called dnaMatch that takes these two DNA sequences as input and returns true if they are a match and false otherwise. Two DNA sequences are considered a match if they have the same characters in the same order. + +**Example** + +``` +If the input DNA sequences are "ACGT" and "ACGT", the function should return true because they are an exact match. +If the input DNA sequences are "ACGT" and "ACTG", the function should return false because the order of characters is different. +If the input DNA sequences are "AACT" and "AACC", the function should return true because some characters do not match. +``` + +**Here's an example of how the function should work:** + +```javascript +console.log(dnaMatch("ACGT", "ACGT")); // true +console.log(dnaMatch("ACGT", "ACTG")); // false +console.log(dnaMatch("AACT", "AACC")); // false +``` + +## Solutions + +```javascript +function dnaMatch(dna1, dna2) { + // Check if the lengths of the two DNA sequences are different + if (dna1.length !== dna2.length) { + return false; // Different lengths, not a match + } + + // Iterate through each character of the DNA sequences + for (let i = 0; i < dna1.length; i++) { + // Compare characters at the current position + if (dna1[i] !== dna2[i]) { + return false; // Characters don't match, not a match + } + } + + // If we reach this point, all characters match, so it's a match + return true; +} + +// Test cases +console.log(dnaMatch("ACGT", "ACGT")); // true +console.log(dnaMatch("ACGT", "ACTG")); // false +console.log(dnaMatch("AACT", "AACC")); // false +``` + +## How it works + +1. The `dnaMatch` function takes two input arguments: `dna1` and `dna2`, which are the two DNA sequences we want to compare. + +2. First, it checks if the lengths of `dna1` and `dna2` are different using the `if (dna1.length !== dna2.length)` conditional statement. If they have different lengths, the function immediately returns `false` because sequences of different lengths cannot be a match. + +3. If the lengths are the same, the function proceeds to compare the characters of the two DNA sequences. + +4. It enters a `for` loop that iterates through the characters of the sequences. The loop is controlled by the variable `i`, which starts at 0 and goes up to `dna1.length - 1`. This loop allows us to compare characters at the same position in both sequences. + +5. Inside the loop, the code compares the characters at the current position `i` using the conditional statement `if (dna1[i] !== dna2[i])`. If the characters at position `i` are not the same in both sequences, the function immediately returns `false` because it found a mismatch. + +6. If the loop completes without finding any mismatches, meaning it has compared all characters in both sequences, the function returns `true`. This indicates that the two DNA sequences are identical and, therefore, a match. + +## References + +- [ChatGPT](https://chat.openai.com/) + +## Problem Added By + +- [Tipchan](https://github.com/tsongtheng) + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/L-I/0011 ThreeSum/README.md b/L-I/0011 ThreeSum/README.md new file mode 100644 index 0000000..b4547d9 --- /dev/null +++ b/L-I/0011 ThreeSum/README.md @@ -0,0 +1,127 @@ +# 0011 ThreeSum ( L-I ) + +## Problem +Three Sum (3SUM) + +Given an array nums of n integers, are there elements  `a, b, c`   in nums such that   `a + b + c = target` ? Find all unique triplets in the array which gives the sum is equal to  `target`  . + + + +## Solution + +```javascript +// 1. Brute Force Solution +const threeSum_brute_Force = (nums, target) => { + let result = []; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + for (let k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] === target) { + result.push([nums[i], nums[j], nums[k]]); + } + } + } + } + return result; +} + +// 2. Using a hash table +const threeSum_hash_table = (nums, target) => { + let result = []; + const hash = {}; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + const complement = target - (nums[i] + nums[j]); + if (complement in hash) { + result.push([nums[i], nums[j], complement]); + } else { + hash[nums[j]] = true; + } + } + } + + return result; +} + +// 3. Two Pointer Technique +const threeSum_two_pointer = (nums, target) => { + let result = []; + nums.sort((a, b) => a - b); + for (let i = 0; i < nums.length - 2; i++) { + if (i > 0 && nums[i] === nums[i - 1]) continue; + let left = i + 1; + let right = nums.length - 1; + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + if (sum === target) { + result.push([nums[i], nums[left], nums[right]]); + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + left++; + right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + return result; +} +``` + +## How it works +### 1. Brute Force Steps + +1. Initialize an empty array result to store the unique triplets that sum to the target value. + +2. Use three nested loops to iterate through the elements of the nums array. The outermost loop (indexed by i) iterates from the first element to the second-to-last element. + +3. The second loop (indexed by j) iterates from the element after the i-th element to the second-to-last element. + +4. The innermost loop (indexed by k) iterates from the element after the j-th element to the last element of the nums array. + +5. Within the innermost loop, check if the sum of the elements at indices i, j, and k is equal to the target value. + +6. If the sum is equal to the target, create a triplet by selecting the elements at these indices (nums[i], nums[j], nums[k]) and push it into the result array. + +7. Continue iterating through the loops to find all such triplets. + +8. Finally, return the result array containing all unique triplets that sum to the target value. + +### 2. Hash Table Steps + +1. Initialize an empty array result to store the unique triplets. +2. Create an empty hash table hash to store the values encountered in the inner loop. +3. Use two nested loops to iterate through the nums array. +4. Calculate the complement by subtracting the sum of nums[i] and nums[j] from the target. +5. Check if the complement exists in the hash table. +6. If found, add [nums[i], nums[j], complement] to the result array. +7. If not found, store nums[j] in the hash table. +8. Return the result array containing all unique triplets that sum to the target value. + +### 3. Two Pointer Technique Steps + +1. Sort the nums array in ascending order. +2. Iterate through the sorted array with a loop, selecting an element nums[i] as the first element of a potential triplet. +3. Use two pointers, left and right, to search for a pair of elements that sum up to the complement of nums[i] (i.e., target - nums[i]). +4. If found, add [nums[i], nums[left], nums[right]] to the result array, and move both pointers while skipping duplicates. +5. Continue the iteration until all unique triplets that sum to the target are found. +6. Return the result array containing all unique triplets. + + +## References +- [Wikipedia](https://en.wikipedia.org/wiki/3SUM) +- [Leetcode](https://leetcode.com/problems/3sum/) + + +## Problem Added By +- [PortFolio](https://femil-savaliya.netlify.app) +- [GitHub](https://www.github.com/Femil32) +- [LinkedIn](https://www.linkedin.com/in/femil-savaliya) + + +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. \ No newline at end of file diff --git a/L-I/0011 ThreeSum/threeSum.js b/L-I/0011 ThreeSum/threeSum.js new file mode 100644 index 0000000..17cdb52 --- /dev/null +++ b/L-I/0011 ThreeSum/threeSum.js @@ -0,0 +1,58 @@ +// 1. Brute Force Solution +const threeSum_brute_Force = (nums, target) => { + let result = []; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + for (let k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] === target) { + result.push([nums[i], nums[j], nums[k]]); + } + } + } + } + return result; +} + +// 2. Using a hash table +const threeSum_hash_table = (nums, target) => { + let result = []; + const hash = {}; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + const complement = target - (nums[i] + nums[j]); + if (complement in hash) { + result.push([nums[i], nums[j], complement]); + } else { + hash[nums[j]] = true; + } + } + } + + return result; +} + +// 3. Two Pointer Technique +const threeSum_two_pointer = (nums, target) => { + let result = []; + nums.sort((a, b) => a - b); + for (let i = 0; i < nums.length - 2; i++) { + if (i > 0 && nums[i] === nums[i - 1]) continue; + let left = i + 1; + let right = nums.length - 1; + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + if (sum === target) { + result.push([nums[i], nums[left], nums[right]]); + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + left++; + right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + return result; +} \ No newline at end of file diff --git a/L-I/0012 Length converter/README.md b/L-I/0012 Length converter/README.md new file mode 100644 index 0000000..82560ba --- /dev/null +++ b/L-I/0012 Length converter/README.md @@ -0,0 +1,79 @@ +# 0012 lengthConverter ( L-I ) + +## Problem +Create a function that converts a given length value from one unit to another. +The function should: +- Accept a numeric length +- Accept a fromUnit and toUnit +- Support common metric and imperial units +- Return the converted value rounded to 5 decimal places +- Throw an error if an invalid unit is provided + +## Supported Units +- meters +- kilometers +- centimeters +- millimeters +- inches +- feet +- yards +- miles + +## Solution +```js +function lengthConverter(length, fromUnit, toUnit) { + const units = { + "meters": 1, + "kilometers": 1000, + "centimeters": 0.01, + "millimeters": 0.001, + "inches": 0.0254, + "feet": 0.3048, + "yards": 0.9144, + "miles": 1609.34 + }; + + if (!units[fromUnit] || !units[toUnit]) { + throw new Error("Invalid unit"); + } + const meters = length * units[fromUnit]; + console.log((meters / units[toUnit]).toFixed(3)); + return (meters / units[toUnit]).toFixed(3); +} +``` + +## Test Cases +```js +removeDuplicates([2, 4, 8, 4, 2, 6, 9, 2, 6, 8, 10]); +// Output: [2, 4, 8, 6, 9, 10] + +removeDuplicates([1, 1, 1, 4, 0, 6, -2, 2, 6, 7, 10]); +// Output: [1, 4, 0, 6, -2, 2, 7, 10] + +removeDuplicates("zoom"); +// Output: "zom" + +removeDuplicates("hello world"); +// Output: "helo wrd" +``` + +## How it works +- Define a units object where each unit is mapped to its value in meters +- Validate both fromUnit and toUnit +- The input length is first converted into meters +- Then we convert meters into the target unit +- The result is rounded to 5 decimal places using toFixed(5) +- Finally, the converted value is returned + +## References +- [Wikipedia – Unit conversion](https://en.wikipedia.org/wiki/Unit_conversion) +- [MDN – JavaScript Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects) + + +## Problem Added By +- [GitHub](https://github.com/rimsha-shoukat) + +## Contributing +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. \ No newline at end of file diff --git a/L-I/0012 Length converter/lengthConverter.js b/L-I/0012 Length converter/lengthConverter.js new file mode 100644 index 0000000..d6a78f1 --- /dev/null +++ b/L-I/0012 Length converter/lengthConverter.js @@ -0,0 +1,29 @@ +function lengthConverter(length, fromUnit, toUnit) { + const units = { + "meters": 1, + "kilometers": 1000, + "centimeters": 0.01, + "millimeters": 0.001, + "inches": 0.0254, + "feet": 0.3048, + "yards": 0.9144, + "miles": 1609.34 + }; + + if (!units[fromUnit] || !units[toUnit]) { + throw new Error("Invalid unit"); + } + + const meters = length * units[fromUnit]; + console.log((meters / units[toUnit]).toFixed(2)); + return (meters / units[toUnit]).toFixed(2); +} + +lengthConverter(100, "meters", "kilometers"); +lengthConverter(5, "miles", "feet"); +lengthConverter(12, "inches", "centimeters"); +lengthConverter(2500, "centimeters", "meters"); +lengthConverter(3, "kilometers", "miles"); +lengthConverter(10, "feet", "yards"); +lengthConverter(5000, "millimeters", "meters"); +lengthConverter(2, "yards", "inches"); \ No newline at end of file