Javascript-Leetcode-examples

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 34

LEETCODE Javascript Solutions

2667. Create Hello World Function

Write a function createHelloWorld. It should return a new function that always returns "Hello World".

Example 1:

Input: args = []
Output: "Hello World"
Explanation:
const f = createHelloWorld();
f(); // "Hello World"

The function returned by createHelloWorld should always return "Hello World".

Example 2:

Input: args = [{},null,42]


Output: "Hello World"
Explanation:
const f = createHelloWorld();
f({}, null, 42); // "Hello World"

Any arguments could be passed to the function but it should still always return "Hello World".

var createHelloWorld = function() {


return () => "Hello World";
};

2620. Counter

Given an integer n, return a counter function. This counter function initially returns n and then returns 1 more than
the previous value every subsequent time it is called ( n, n + 1, n + 2, etc).

Example 1:

Input:
n = 10
["call","call","call"]
Output: [10,11,12]
Explanation:
counter() = 10 // The first time counter() is called, it returns n.
counter() = 11 // Returns 1 more than the previous time.
counter() = 12 // Returns 1 more than the previous time.

Example 2:

Input:
n = -2
["call","call","call","call","call"]
Output: [-2,-1,0,1,2]
Explanation: counter() initially returns -2. Then increases after each sebsequent call.

var createCounter = function(n) {

return ()=> n++

};

What is Closure ?
 A closure is created when a function is defined inside another function, and the inner
function references variables in the outer function's scope. When the inner function is
returned from the outer function, it retains a reference to the outer function's scope, and can
continue to access those variables even after the outer function has finished executing. Vice-
Versa is not true!!
 In simple terms a closure can "remember" values from its outer function's scope and use
them later, even if the outer function has returned and those values would normally be out
of scope.

When to use closure concept ?


FIrst let's summarize the definition as usually the definition gives the answer for when to use..

 From definition you can see that it's used for retrival of values from outer parent function so
we can understand that closure can be used for retrival of dead values which have become
out of scope. also we can comprehend that it can used for privating some varibles or function.
 Thus closures are useful for creating private variables and functions, implementing
partial function application, and preserving state in asynchronous code.
 While writing the code whenever there is a need for these types of thing try to incorporate
this closure concept i.e. In a programmer languge it's called lexical environment

Where and How to use closure concept ?


1. Private variables and functions:
const makeCounter = () => {
let count = 0;

return () => {
count++;
console.log(count);
}
}

let counter = makeCounter();


counter(); // logs 1
counter(); // logs 2
counter(); // logs 3

 In this example, makeCounter is an arrow function that returns another arrow function. The returned
function increments a count variable each time it is called, and logs the new value of count to
the console.
 When makeCounter is called, it creates a new scope with a count variable initialized to 0. It then
returns a new arrow function that "closes over" this scope and increments the count
variable each time it is called.
 When we assign the returned arrow function to the counter variable, we create a closure that
retains a reference to the count variable.
 Each time we call counter(), it increments the count variable and logs the new value to the
console, because it is still "closing over" the original count variable in the outer function's
scope.
 Thus because the count variable is not exposed outside of the returned object, it is effectively
a private variable that can only be accessed or modified through the makeCounter() methods.

2. Partial function:
I was introduced to this concept name during development phase but was shocked that
unknowingly I have used it many times. I'm sure that you all also must have use this:
function add(x) {
return function(y) {
return x + y;
}
}

let add5 = add(5);


console.log(add5(3)); // 8

 In this example, the add() function returns another function that takes a single argument and
returns the sum of that argument and the x value from the outer function's scope.
 This allows us to "partially apply" the add() function by passing in an x value and getting back
a new function that always adds that value to its argument.
 Thuse we can then use the new function like any other function, passing in different y values
as needed.
3. For preserving states in asynchronous code:
The below snippet is from my personal project:)
const animate = (element, from, to, duration) => {
let start = performance.now();

const update = () => {


let time = performance.now() - start;
let progress = time / duration;
let value = from + (to - from) * progress;

element.style.left = value + 'px';

if (progress < 1) {
requestAnimationFrame(update);
}
}

requestAnimationFrame(update);
}

let element = document.getElementById('my-element');


animate(element, 0, 100, 1000);

 In this example, the animate() function creates a closure over the start variable, which is used to
calculate the elapsed time since the animation started.
 The update() function also "closes over" the element, from, to, and duration arguments, so that it
can use them to update the element's position over time.
 Thus by creating a closure over these values, we can preserve their state between
animation frames, even though the update() function is
called asynchronously by requestAnimationFrame().

To Be Or Not To Be

Write a function expect that helps developers test their code. It should take in any value val and return an object
with the following two functions.

 toBe(val) accepts another value and returns true if the two values === each other. If they are not equal, it
should throw an error "Not Equal".
 notToBe(val) accepts another value and returns true if the two values !== each other. If they are equal, it
should throw an error "Equal".

Example 1:

Input: func = () => expect(5).toBe(5)


Output: {"value": true}
Explanation: 5 === 5 so this expression returns true.
Example 2:

Input: func = () => expect(5).toBe(null)


Output: {"error": "Not Equal"}
Explanation: 5 !== null so this expression throw the error "Not Equal".

Example 3:

Input: func = () => expect(5).notToBe(null)


Output: {"value": true}
Explanation: 5 !== null so this expression returns true.

Solution:

The problem requires the expect function to support making toBe and notToBe calls (e.g., expect(5).toBe(5); should
return true, and expect(5).notToBe(5); should throw "Equal"). To do so, we should define the return of the expect
function based on what kind of call we make. This can be done in the following format:

return {

toBe: (parameters) => {

[doing some stuff]

},

notToBe: (parameters) => {

[doing some stuff]

The below solution follows this format, where toBe and notToBe both has an if else statement, such that if the throw
condition is true, throw an error. Otherwise, return true.

Code

var expect = function(val) {

return {

toBe: (val2) => {

if (val !== val2) throw new Error("Not Equal");

else return true;

},

notToBe: (val2) => {


if (val === val2) throw new Error("Equal");

else return true;

};

/* For example, when expect(5).toBe(4) is called,

val is the expect parameter (so val equals 5),

val2 is the toBe parameter (so val2 equals 4).

Since val !== val2, aka 5 != 4, we throw error "Not Equal". */

Counter II

Write a function createCounter. It should accept an initial integer init. It should return an object with three functions.

The three functions are:

 increment() increases the current value by 1 and then returns it.


 decrement() reduces the current value by 1 and then returns it.
 reset() sets the current value to init and then returns it.

Example 1:

Input: init = 5, calls = ["increment","reset","decrement"]


Output: [6,5,4]
Explanation:
const counter = createCounter(5);
counter.increment(); // 6
counter.reset(); // 5
counter.decrement(); // 4

Example 2:

Input: init = 0, calls = ["increment","increment","decrement","reset","reset"]


Output: [1,2,1,0,0]
Explanation:
const counter = createCounter(0);
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
counter.reset(); // 0
counter.reset(); // 0
Solution:

1. Using Traditional Function


 A traditional function is defined using the function keyword. It can be a named function or
an anonymous function assigned to a variable.
 In the following code we have created three functions increment(), decrement() and reset() using
the function keyword. Inside this function we need to do the appropriate operation that was instructed
in the question i.e. for increment() we need to return the currentvalue+1, similarly in decrement() we need
to return the currentvalue-1 and in reset() we need to assign originalvalue to the currentvalue.
var createCounter = function(init) {
let presentCount = init;

function increment() {
return ++presentCount;
}

function decrement() {
return --presentCount;
}

function reset() {
return (presentCount = init);
}

return { increment, decrement, reset };


};

Time and Space: O(1)

2. Using Arrow Function


 An arrow function is a shorter syntax for defining functions, introduced in ES6.
 It uses the => syntax instead of the function keyword, and has some differences in behavior
compared to traditional functions, such as inheriting the this value from the surrounding context
 For better understanding please read this posts : Arrow function(6min read) by @Jatin and Closure
on Counter-1(8min read) problem by @Jatin
var createCounter = function(init) {
let presentCount = init
return {
increment:()=> ++presentCount,
decrement:()=> --presentCount,
reset:()=> presentCount = init,
}
};

Time and Space: O(1)


3. Using Class
 A class is a template for creating objects with a set of properties and methods.

 In ES6, classes were introduced as syntactic sugar over the prototype-based inheritance model
but shortly after that It provided a way to support inheritance and can have static
methods and properties, getters and setters, and more. Thus they provided a way to write
object-oriented code in a more concise and organized way.

 In the following example the Couter is the name of the class.

o The constructor method is a special method that is called when an object is created based on
the class.
o It initializes the object with properties init and presentCount.
The increment(), decrement()and reset() method are regular methods that can be called on an
instance of the Counter class to get the output
o To create an object based on a class we use the new operator i.e. we create an object
called createCounter based on the Counter class, passing in the init value as arguments to the
constructor.
class Counter {
constructor(init) {
this.init = init;
this.presentCount = init;
}

increment() {
this.presentCount += 1;
return this.presentCount;
}

decrement() {
this.presentCount -= 1;
return this.presentCount;
}

reset() {
this.presentCount = this.init;
return this.presentCount;
}
}

var createCounter = function(init) {


return new Counter(init);
};

Time and Space: O(1)


In conclusion which one is the better way??
 Classes are useful for creating objects with shared behavior.
 Traditional functions are useful for reusable chunks of code
 Arrow functions are useful for short, concise functions or when preserving the value of this is
important.
 Thus, I believe that classes are the best way to implement this types of problems in real life as they
give flexibility of scaling with the shared behaviour properties.

Apply Transform Over Each Element in Array

Given an integer array arr and a mapping function fn, return a new array with a transformation applied to each
element.

The returned array should be created such that returnedArray[i] = fn(arr[i], i).

Please solve it without the built-in Array.map method.

Example 1:

Input: arr = [1,2,3], fn = function plusone(n) { return n + 1; }


Output: [2,3,4]
Explanation:
const newArray = map(arr, plusone); // [2,3,4]
The function increases each value in the array by one.

Example 2:

Input: arr = [1,2,3], fn = function plusI(n, i) { return n + i; }


Output: [1,3,5]
Explanation: The function increases each value by the index it resides in.

Example 3:

Input: arr = [10,20,30], fn = function constant() { return 42; }


Output: [42,42,42]
Explanation: The function always returns 42.

Solution:

1. Using a for loop with operational container:


 We can loop through each element of the input array and apply the map function to each
element. We then store the transformed values in a new array.
var map = function(arr, fn) {
const transformedArr = [];
for (let i = 0; i < arr.length; i++) {
transformedArr[i] = fn(arr[i], i);
}
return transformedArr;
};

TC and SC: O(n)

Using a for loop without any container a.k.a Inmemory transformations

var map = function(arr, fn) {


for (let i = 0; i < arr.length; ++i) {
arr[i] = fn(arr[i], i);
}
return arr;
};

Filter Elements from Array

Given an integer array arr and a filtering function fn, return a filtered array filteredArr.

The fn function takes one or two arguments:

 arr[i] - number from the arr


 i - index of arr[i]

filteredArr should only contain the elements from the arr for which the expression fn(arr[i], i) evaluates to
a truthy value. A truthy value is a value where Boolean(value) returns true.

Please solve it without the built-in Array.filter method.

Example 1:

Input: arr = [0,10,20,30], fn = function greaterThan10(n) { return n > 10; }


Output: [20,30]
Explanation:
const newArray = filter(arr, fn); // [20, 30]
The function filters out values that are not greater than 10

Example 2:
Input: arr = [1,2,3], fn = function firstIndex(n, i) { return i === 0; }
Output: [1]
Explanation:
fn can also accept the index of each element
In this case, the function removes elements not at index 0

Solution:

Code in JavaScript
/**
* @param {number[]} arr
* @param {Function} fn
* @return {number[]}
*/
var filter = function(arr, fn) {
var filteredArr = [];
for (var i = 0; i < arr.length; i++) {
if (fn(arr[i], i)) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
};

Code in TypeScript
type FilterFn<T> = (element: T, index?: number, array?: T[]) => boolean;

function filter<T>(arr: T[], fn: FilterFn<T>): T[] {


const filteredArr: T[] = [];
for (let i = 0; i < arr.length; i++) {
if (fn(arr[i], i, arr)) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
}

Array Reduce Transformation

Given an integer array nums, a reducer function fn, and an initial value init, return a reduced array.

A reduced array is created by applying the following operation: val = fn(init, nums[0]), val = fn(val, nums[1]), val =
fn(val, nums[2]), ... until every element in the array has been processed. The final value of val is returned.

If the length of the array is 0, it should return init.

Please solve it without using the built-in Array.reduce method.


Example 1:

Input:
nums = [1,2,3,4]
fn = function sum(accum, curr) { return accum + curr; }
init = 0
Output: 10
Explanation:
initially, the value is init=0.
(0) + nums[0] = 1
(1) + nums[1] = 3
(3) + nums[2] = 6
(6) + nums[3] = 10
The final answer is 10.

Example 2:

Input:
nums = [1,2,3,4]
fn = function sum(accum, curr) { return accum + curr * curr; }
init = 100
Output: 130
Explanation:
initially, the value is init=100.
(100) + nums[0]^2 = 101
(101) + nums[1]^2 = 105
(105) + nums[2]^2 = 114
(114) + nums[3]^2 = 130
The final answer is 130.

Solution:

Code In JavaScript
/**
* @param {number[]} nums
* @param {Function} fn
* @param {number} init
* @return {number}
*/
var reduce = function(nums, fn, init) {
let val = init;
for (let i = 0; i < nums.length; i++) {
val = fn(val, nums[i]);
}
return val;
};

Code In TypeScript
type Reducer<T, U> = (acc: T, curr: U) => T;

function reduce<T, U>(nums: U[], fn: Reducer<T, U>, init: T): T {


let val: T = init;
for (let i = 0; i < nums.length; i++) {
val = fn(val, nums[i]);
}
return val;
}

Function Composition

Given an array of functions [f1, f , f , ..., f ], return a new function fn that is the function composition of the array of
2 3 n

functions.

The function composition of [f(x), g(x), h(x)] is fn(x) = f(g(h(x))).

The function composition of an empty list of functions is the identity function f(x) = x.

You may assume each function in the array accepts one integer as input and returns one integer as output.

Example 1:

Input: functions = [x => x + 1, x => x * x, x => 2 * x], x = 4


Output: 65
Explanation:
Evaluating from right to left ...
Starting with x = 4.
2 * (4) = 8
(8) * (8) = 64
(64) + 1 = 65

Example 2:

Input: functions = [x => 10 * x, x => 10 * x, x => 10 * x], x = 1


Output: 1000
Explanation:
Evaluating from right to left ...
10 * (1) = 10
10 * (10) = 100
10 * (100) = 1000

Solution:

Code In JS
/**
* @param {Function[]} functions
* @return {Function}
*/
var compose = function(functions) {
if (functions.length === 0) {
return function(x) { return x; };
}

return functions.reduceRight(function(prevFn, nextFn) {


return function(x) {
return nextFn(prevFn(x));
};
});

};

const fn = compose([x => x + 1, x => 2 * x]);


console.log(fn(4)); // 9

Return Length of Arguments Passed

Write a function argumentsLength that returns the count of arguments passed to it.

Example 1:

Input: argsArr = [5]


Output: 1
Explanation:
argumentsLength(5); // 1

One value was passed to the function so it should return 1.

Example 2:

Input: argsArr = [{}, null, "3"]


Output: 3
Explanation:
argumentsLength({}, null, "3"); // 3

Three values were passed to the function so it should return 3.

Solution:
var argumentsLength = function(...args) {
return args.length
};

/**
* argumentsLength(1, 2, 3); // 3
*/

Allow One Function Call


Given a function fn, return a new function that is identical to the original function except that it ensures fn is called
at most once.

 The first time the returned function is called, it should return the same result as fn.
 Every subsequent time it is called, it should return undefined.

Example 1:

Input: fn = (a,b,c) => (a + b + c), calls = [[1,2,3],[2,3,6]]


Output: [{"calls":1,"value":6}]
Explanation:
const onceFn = once(fn);
onceFn(1, 2, 3); // 6
onceFn(2, 3, 6); // undefined, fn was not called

Example 2:

Input: fn = (a,b,c) => (a * b * c), calls = [[5,7,4],[2,3,6],[4,6,8]]


Output: [{"calls":1,"value":140}]
Explanation:
const onceFn = once(fn);
onceFn(5, 7, 4); // 140
onceFn(2, 3, 6); // undefined, fn was not called
onceFn(4, 6, 8); // undefined, fn was not called

Solution:
/**
* @param {Function} fn
* @return {Function}
*/
var once = function(fn) {

let hasBeenCalled = false;


let result;

return function(...args) {
if (!hasBeenCalled) {
result = fn(...args);
hasBeenCalled = true;
return result;
} else {
return undefined;
}
}

};

let fn = (a,b,c) => (a + b + c);


let onceFn = once(fn);

console.log(onceFn(1,2,3)); // 6
console.log(onceFn(2,3,6)); // undefined

Memoize

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.

You can assume there are 3 possible input functions: sum, fib, and factorial.

 sum accepts two integers a and b and returns a + b.


 fib accepts a single integer n and returns 1 if n <= 1 or fib(n - 1) + fib(n - 2) otherwise.
 factorial accepts a single integer n and returns 1 if n <= 1 or factorial(n - 1) * n otherwise.

Example 1:

Input
"sum"
["call","call","getCallCount","call","getCallCount"]
[[2,2],[2,2],[],[1,2],[]]
Output
[4,4,1,3,2]

/**
* @param {Function} fn
*/
function memoize(fn) {

const cache = {};

return function(...args) {
const key = JSON.stringify(args);

if (key in cache) {
return cache[key];
}

const result = fn.apply(this, args);


cache[key] = result;

return result;
}
}

const memoizedSum = memoize(function(a, b) {


return a + b;
});

console.log(memoizedSum(2, 3)); // Output: Computing sum, 5


console.log(memoizedSum(2, 3)); // Output: 5

Add Two Promises

Given two promises promise1 and promise2, return a new promise. promise1 and promise2 will both resolve with a
number. The returned promise should resolve with the sum of the two numbers.

Example 1:

Input:
promise1 = new Promise(resolve => setTimeout(() => resolve(2), 20)),
promise2 = new Promise(resolve => setTimeout(() => resolve(5), 60))
Output: 7
Explanation: The two input promises resolve with the values of 2 and 5 respectively. The returned promise should
resolve with a value of 2 + 5 = 7. The time the returned promise resolves is not judged for this problem.

Example 2:

Input:
promise1 = new Promise(resolve => setTimeout(() => resolve(10), 50)),
promise2 = new Promise(resolve => setTimeout(() => resolve(-12), 30))
Output: -2
Explanation: The two input promises resolve with the values of 10 and -12 respectively. The returned promise
should resolve with a value of 10 + -12 = -2.

Solution:
/**
* @param {Promise} promise1
* @param {Promise} promise2
* @return {Promise}
*/
var addTwoPromises = async function(promise1, promise2) {
// Wait for both promises to resolve and retrieve their values
const [value1, value2] = await Promise.all([promise1, promise2]);

// Return a new promise that resolves with the sum of the values
return value1 + value2;
};
// // Example usage:
// var promise1 = new Promise(resolve => setTimeout(() => resolve(2), 20));
// var promise2 = new Promise(resolve => setTimeout(() => resolve(5), 60));

// addTwoPromises(promise1, promise2)
// .then(console.log); // Output: 7

Sleep

Given a positive integer millis, write an asynchronous function that sleeps for millis milliseconds. It can resolve any
value.

Example 1:

Input: millis = 100


Output: 100
Explanation: It should return a promise that resolves after 100ms.
let t = Date.now();
sleep(100).then(() => {
console.log(Date.now() - t); // 100
});

Example 2:

Input: millis = 200


Output: 200
Explanation: It should return a promise that resolves after 200ms.

Solution:

/**
* @param {number} millis
*/
async function sleep(millis) {
await new Promise(resolve => setTimeout(resolve, millis));
}

/**
* let t = Date.now()
* sleep(100).then(() => console.log(Date.now() - t)) // 100
*/

Promise Time Limit


Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input
function. fn takes arguments provided to the time limited function.

The time limited function should follow these rules:

 If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the
result.
 If the execution of the fn exceeds the time limit, the time limited function should reject with the
string "Time Limit Exceeded".

Example 1:

Input:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 50
Output: {"rejected":"Time Limit Exceeded","time":50}
Explanation:
const limited = timeLimit(fn, t)
const start = performance.now()
let result;
try {
const res = await limited(...inputs)
result = {"resolved": res, "time": Math.floor(performance.now() - start)};
} catch (err) {
result = {"rejected": err, "time": Math.floor(performance.now() - start)};
}
console.log(result) // Output

The provided function is set to resolve after 100ms. However, the time limit is set to 50ms. It rejects at t=50ms
because the time limit was reached.

Example 2:

Input:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 150
Output: {"resolved":25,"time":100}
Explanation:
The function resolved 5 * 5 = 25 at t=100ms. The time limit is never reached.

Solution:
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
var timeLimit = function(fn, t) {
return async function(...args) {
const originalFnPromise = fn(...args);

const timeoutPromise = new Promise((_, reject) => {


setTimeout(() => {
reject('Time Limit Exceeded')
}, t);
})

return Promise.race([originalFnPromise, timeoutPromise]);


}
};

Debounce

Given a function fn and a time in milliseconds t, return a debounced version of that function.

A debounced function is a function whose execution is delayed by t milliseconds and whose execution is cancelled
if it is called again within that window of time. The debounced function should also receive the passed parameters.

For example, let's say t = 50ms, and the function was called at 30ms, 60ms, and 100ms. The first 2 function calls
would be cancelled, and the 3rd function call would be executed at 150ms. If instead t = 35ms, The 1st call would
be cancelled, the 2nd would be executed at 95ms, and the 3rd would be executed at 135ms.

The above diagram shows how debounce will transform events. Each rectangle represents 100ms and the
debounce time is 400ms. Each color represents a different set of inputs.

Please solve it without using lodash's _.debounce() function.

Example 1:
Input:
t = 50
calls = [
{"t": 50, inputs: [1]},
{"t": 75, inputs: [2]}
]
Output: [{"t": 125, inputs: [2]}]

Solution:
var debounce = function(fn, t = 1000) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), t);
}
};

Execute Asynchronous Functions in Parallel

Given an array of asynchronous functions functions, return a new promise promise. Each function in the array
accepts no arguments and returns a promise.

promise resolves:

 When all the promises returned from functions were resolved successfully. The resolved value
of promise should be an array of all the resolved values of promises in the same order as they were in
the functions.

promise rejects:

 When any of the promises returned from functions were rejected. promise should also reject with the reason
of the first rejection.

Please solve it without using the built-in Promise.all function.

Example 1:

Input: functions = [
() => new Promise(resolve => setTimeout(() => resolve(5), 200))
]
Output: {"t": 200, "resolved": [5]}
Explanation:
promiseAll(functions).then(console.log); // [5]

The single function was resolved at 200ms with a value of 5.


Example 2:

Input: functions = [
() => new Promise(resolve => setTimeout(() => resolve(1), 200)),
() => new Promise((resolve, reject) => setTimeout(() => reject("Error"), 100))
]
Output: {"t": 100, "rejected": "Error"}
Explanation: Since one of the promises rejected, the returned promise also rejected with the same error at the
same time.

Solution:
/**
* @param {Array<Function>} functions
* @return {Promise<any>}
*/
var promiseAll = async function(functions) {
return new Promise((resolve, reject) => {
// We know the resulting array will be the same length as functions
const results = new Array(functions.length);
let count = 0;
functions.forEach((fn, i) => {
fn()
.then(val => {
results[i] = val;
count++;
if(count === functions.length) resolve(results);
})
.catch(reason => reject(reason));
});
});
};

/**
* const promise = promiseAll([() => new Promise(res => res(42))])
* promise.then(console.log); // [42]
*/

Is Object Empty

Given an object or an array, return if it is empty.

 An empty object contains no key-value pairs.


 An empty array contains no elements.

You may assume the object or array is the output of JSON.parse.

Example 1:
Input: obj = {"x": 5, "y": 42}
Output: false
Explanation: The object has 2 key-value pairs so it is not empty.

Example 2:

Input: obj = {}
Output: true
Explanation: The object doesn't have any key-value pairs so it is empty.

Example 3:

Input: obj = [null, false, 0]


Output: false
Explanation: The array has 3 elements so it is not empty.

var isEmpty = function(obj) {


for (const _ in obj) return false;
return true;
};

Chunk Array

Given an array arr and a chunk size size, return a chunked array. A chunked array contains the original elements
in arr, but consists of subarrays each of length size. The length of the last subarray may be less
than size if arr.length is not evenly divisible by size.

You may assume the array is the output of JSON.parse. In other words, it is valid JSON.

Please solve it without using lodash's _.chunk function.

Example 1:

Input: arr = [1,2,3,4,5], size = 1


Output: [[1],[2],[3],[4],[5]]
Explanation: The arr has been split into subarrays each with 1 element.

Example 2:

Input: arr = [1,9,6,3,2], size = 3


Output: [[1,9,6],[3,2]]
Explanation: The arr has been split into subarrays with 3 elements. However, only two elements are left for the
2nd subarray.
chunk=f=(a,n,r=[],i=0)=>
a.length-i?
((r[i/n|0]=r[i/n|0]??[])[i%n]=a[i++],f(a,n,r,i))
:r

Array Prototype Last

Write code that enhances all arrays such that you can call the array.last() method on any array and it will return the
last element. If there are no elements in the array, it should return -1.

You may assume the array is the output of JSON.parse.

Example 1:

Input: nums = [null, {}, 3]


Output: 3
Explanation: Calling nums.last() should return the last element: 3.

Example 2:

Input: nums = []
Output: -1
Explanation: Because there are no elements, return -1

Array.prototype.last = function() {
if (this.length === 0) {
return -1;
} else {
return this[this.length - 1];
}
};

Group By

Write code that enhances all arrays such that you can call the array.groupBy(fn) method on any array and it will
return a grouped version of the array.

A grouped array is an object where each key is the output of fn(arr[i]) and each value is an array containing all
items in the original array with that key.

The provided callback fn will accept an item in the array and return a string key.
The order of each value list should be the order the items appear in the array. Any order of keys is acceptable.

Please solve it without lodash's _.groupBy function.

Example 1:

Input:
array = [
{"id":"1"},
{"id":"1"},
{"id":"2"}
],
fn = function (item) {
return item.id;
}
Output:
{
"1": [{"id": "1"}, {"id": "1"}],
"2": [{"id": "2"}]
}

/**
* @param {Function} fn
* @return {Array}
*/
Array.prototype.groupBy = function(fn) {
// Reduce the array into a single object
return this.reduce((grouped, item) => {
// Apply the provided callback function to get the key
const key = fn(item);

// If the key doesn't exist in the grouped object, create a new array for it
if (!grouped[key]) {
grouped[key] = [];
}

// Push the current item to the array associated with the key
grouped[key].push(item);

// Return the updated grouped object for the next iteration


return grouped;
}, {});
};

/**
* [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]}
*/
Sort By

Given an array arr and a function fn, return a sorted array sortedArr. You can assume fn only returns numbers and
those numbers determine the sort order of sortedArr. sortedArray must be sorted in ascending order by fn output.

You may assume that fn will never duplicate numbers for a given array.

Example 1:

Input: arr = [5, 4, 1, 2, 3], fn = (x) => x


Output: [1, 2, 3, 4, 5]
Explanation: fn simply returns the number passed to it so the array is sorted in ascending order.

Example 2:

Input: arr = [{"x": 1}, {"x": 0}, {"x": -1}], fn = (d) => d.x
Output: [{"x": -1}, {"x": 0}, {"x": 1}]
Explanation: fn returns the value for the "x" key. So the array is sorted based on that value.

/**
* @param {Array} arr
* @param {Function} fn
* @return {Array}
*/
const sortBy = (arr, fn) => Array.from(arr).sort((a, b) => fn(a) > fn(b) ? 1 : -1);

Join Two Arrays by ID

Given two arrays arr1 and arr2, return a new array joinedArray. All the objects in each of the two inputs arrays will
contain an id field that has an integer value. joinedArray is an array formed by merging arr1 and arr2 based
on their id key. The length of joinedArray should be the length of unique values of id. The returned array should be
sorted in ascending order based on the id key.

If a given id exists in one array but not the other, the single object with that id should be included in the result
array without modification.

If two objects share an id, their properties should be merged into a single object:

 If a key only exists in one object, that single key-value pair should be included in the object.
 If a key is included in both objects, the value in the object from arr2 should override the value from arr1.

Example 1:
Input:
arr1 = [
{"id": 1, "x": 1},
{"id": 2, "x": 9}
],
arr2 = [
{"id": 3, "x": 5}
]
Output:
[
{"id": 1, "x": 1},
{"id": 2, "x": 9},
{"id": 3, "x": 5}
]
Explanation: There are no duplicate ids so arr1 is simply concatenated with arr2.

Example 2:

Input:
arr1 = [
{"id": 1, "x": 2, "y": 3},
{"id": 2, "x": 3, "y": 6}
],
arr2 = [
{"id": 2, "x": 10, "y": 20},
{"id": 3, "x": 0, "y": 0}
]
Output:
[
{"id": 1, "x": 2, "y": 3},
{"id": 2, "x": 10, "y": 20},
{"id": 3, "x": 0, "y": 0}
]

/**
* @param {Array} arr1
* @param {Array} arr2
* @return {Array}
*/
var join = function(arr1, arr2) {
const result = {};
for (let i = 0; i < arr1.length; i++) {
result[arr1[i].id] = arr1[i];
}
for (let i = 0; i < arr2.length; i++) {
if (result[arr2[i].id]) {
for (const key in arr2[i]) result[arr2[i].id][key] = arr2[i][key];
} else {
result[arr2[i].id] = arr2[i];
}
}

return Object.values(result);
};
Flatten Deeply Nested Array

Given a multi-dimensional array arr and a depth n, return a flattened version of that array.

A multi-dimensional array is a recursive data structure that contains integers or other multi-dimensional arrays.

A flattened array is a version of that array with some or all of the sub-arrays removed and replaced with the actual
elements in that sub-array. This flattening operation should only be done if the current depth of nesting is
less than n. The depth of the elements in the first array are considered to be 0.

Please solve it without the built-in Array.flat method.

Example 1:

Input
arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]
n=0
Output
[1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]

Explanation
Passing a depth of n=0 will always result in the original array. This is because the smallest possible depth of a
subarray (0) is not less than n=0. Thus, no subarray should be flattened.

Example 2:

Input
arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]
n=1
Output
[1, 2, 3, 4, 5, 6, 7, 8, [9, 10, 11], 12, 13, 14, 15]

/**
* @param {any[]} arr
* @param {number} depth
* @return {any[]}
*/
var flat = function(arr, depth) {
const stack = [...arr.map(item => [item, depth])];
const result = [];

while (stack.length > 0) {


const [item, depth] = stack.pop();

if (Array.isArray(item) && depth > 0) {


stack.push(...item.map(subItem => [subItem, depth - 1]));
} else {
result.push(item);
}
}

return result.reverse();
};

Compact Object

Given an object or array obj, return a compact object. A compact object is the same as the original object, except
with keys containing falsy values removed. This operation applies to the object and any nested objects. Arrays are
considered objects where the indices are keys. A value is considered falsy when Boolean(value) returns false.

You may assume the obj is the output of JSON.parse. In other words, it is valid JSON.

Example 1:

Input: obj = [null, 0, false, 1]


Output: [1]
Explanation: All falsy values have been removed from the array.

Example 2:

Input: obj = {"a": null, "b": [false, 1]}


Output: {"b": [1]}
Explanation: obj["a"] and obj["b"][0] had falsy values and were removed.

var compactObject = function(obj) {


// These three if statements deal with when obj is not an iterable object
// Steps 1-3 as described above
if (obj === null) return null;
if (Array.isArray(obj)) return obj.filter(Boolean).map(compactObject);
if (typeof obj !== "object") return obj;

// This for loop deals with when obj is an iterable object


// Steps 4-5 as described above
const compacted = {};
for (const key in obj) {
let value = compactObject(obj[key]);
if (Boolean(value)) compacted[key] = value;
}

return compacted;
};
Event Emitter

Design an EventEmitter class. This interface is similar (but with some differences) to the one found in Node.js or the
Event Target interface of the DOM. The EventEmitter should allow for subscribing to events and emitting them.

Your EventEmitter class should have the following two methods:

 subscribe - This method takes in two arguments: the name of an event as a string and a callback function.
This callback function will later be called when the event is emitted.
An event should be able to have multiple listeners for the same event. When emitting an event with
multiple callbacks, each should be called in the order in which they were subscribed. An array of results
should be returned. You can assume no callbacks passed to subscribe are referentially identical.
The subscribe method should also return an object with an unsubscribe method that enables the user to
unsubscribe. When it is called, the callback should be removed from the list of subscriptions
and undefined should be returned.
 emit - This method takes in two arguments: the name of an event as a string and an optional array of
arguments that will be passed to the callback(s). If there are no callbacks subscribed to the given event,
return an empty array. Otherwise, return an array of the results of all callback calls in the order they were
subscribed.

Example 1:

Input:
actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"],
values = [[], ["firstEvent", "function cb1() { return 5; }"], ["firstEvent", "function cb1() { return 6; }"], ["firstEvent"]]
Output: [[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
Explanation:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], no callback are subscribed yet
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], returns the output of cb1 and cb2

Example 2:

Input:
actions = ["EventEmitter", "subscribe", "emit", "emit"],
values = [[], ["firstEvent", "function cb1(...args) { return args.join(','); }"], ["firstEvent", [1,2,3]], ["firstEvent", [3,4,6]]]
Output: [[],["subscribed"],["emitted",["1,2,3"]],["emitted",["3,4,6"]]]
Explanation: Note that the emit method should be able to accept an OPTIONAL array of arguments.

class EventEmitter {
constructor() {
this.events = new Map();
}

subscribe(event, cb) {
if (!this.events.has(event)) {
this.events.set(event, []);
}

const listeners = this.events.get(event);


listeners.push(cb);

return {
unsubscribe: () => {
const index = listeners.indexOf(cb);
if (index !== -1) {
listeners.splice(index, 1);
}
}
};
}

emit(event, args = []) {


if (!this.events.has(event)) {
return [];
}

const listeners = this.events.get(event);


const results = [];

for (const listener of listeners) {


results.push(listener(...args));
}

return results;
}
}

Array Wrapper

Create a class ArrayWrapper that accepts an array of integers in its constructor. This class should have two features:

 When two instances of this class are added together with the + operator, the resulting value is the sum of
all the elements in both arrays.
 When the String() function is called on the instance, it will return a comma separated string surrounded by
brackets. For example, [1,2,3].

Example 1:

Input: nums = [[1,2],[3,4]], operation = "Add"


Output: 10
Explanation:
const obj1 = new ArrayWrapper([1,2]);
const obj2 = new ArrayWrapper([3,4]);
obj1 + obj2; // 10
Example 2:

Input: nums = [[23,98,42,70]], operation = "String"


Output: "[23,98,42,70]"
Explanation:
const obj = new ArrayWrapper([23,98,42,70]);
String(obj); // "[23,98,42,70]"

/**
* @param {number[]} nums
*/
var ArrayWrapper = function(nums) {
this.nums = nums;
};

ArrayWrapper.prototype.valueOf = function() {
return this.nums.reduce((sum, num) => sum + num, 0);
}

ArrayWrapper.prototype.toString = function() {
return `[${this.nums.join(',')}]`;
}

/**
* const obj1 = new ArrayWrapper([1,2]);
* const obj2 = new ArrayWrapper([3,4]);
* obj1 + obj2; // 10
* String(obj1); // "[1,2]"
* String(obj2); // "[3,4]"
*/

Calculator with Method Chaining

Design a Calculator class. The class should provide the mathematical operations of addition, subtraction,
multiplication, division, and exponentiation. It should also allow consecutive operations to be performed using
method chaining. The Calculator class constructor should accept a number which serves as the initial value of result.

Your Calculator class should have the following methods:

 add - This method adds the given number value to the result and returns the updated Calculator.
 subtract - This method subtracts the given number value from the result and returns the updated Calculator.
 multiply - This method multiplies the result by the given number value and returns the updated Calculator.
 divide - This method divides the result by the given number value and returns the updated Calculator. If the
passed value is 0, an error "Division by zero is not allowed" should be thrown.
 power - This method raises the result to the power of the given number value and returns the
updated Calculator.
 getResult - This method returns the result.
Solutions within 10 of the actual result are considered correct.
-5

Example 1:

Input:
actions = ["Calculator", "add", "subtract", "getResult"],
values = [10, 5, 7]
Output: 8
Explanation:
new Calculator(10).add(5).subtract(7).getResult() // 10 + 5 - 7 = 8

Example 2:

Input:
actions = ["Calculator", "multiply", "power", "getResult"],
values = [2, 5, 2]
Output: 100
Explanation:
new Calculator(2).multiply(5).power(2).getResult() // (2 * 5) ^ 2 = 100

class Calculator {

/**
* @param {number} value
*/
constructor(value) {
this.result = value;
}

/**
* @param {number} value
* @return {Calculator}
*/
add(value){
this.result += value;
return this;
}

/**
* @param {number} value
* @return {Calculator}
*/
subtract(value){
this.result -= value;
return this;

/**
* @param {number} value
* @return {Calculator}
*/
multiply(value) {
this.result *= value;
return this;

/**
* @param {number} value
* @return {Calculator}
*/
divide(value) {
if (value === 0) {
throw new Error("Division by zero is not allowed");
}
this.result /= value;
return this;
}

/**
* @param {number} value
* @return {Calculator}
*/
power(value) {
this.result **= value;
return this;

/**
* @return {number}
*/
getResult() {
return this.result;

}
}

You might also like