Skip to content

Instantly share code, notes, and snippets.

@MidasXIV
Last active September 20, 2023 07:41
Show Gist options
  • Save MidasXIV/0c3b07279e2b7635be3a7fac89902f89 to your computer and use it in GitHub Desktop.
Save MidasXIV/0c3b07279e2b7635be3a7fac89902f89 to your computer and use it in GitHub Desktop.

Array methods

Q1

Write a function that takes an array of objects, and groups them into an object whose keys are the unique values of a specified property on each object, and whose values are arrays of objects with that value for that property. For example, given the input array

[{name: 'Alice', age: 25}, {name: 'Bob', age: 25}, {name: 'Charlie', age: 30}]

and the property 'age', the function should return.

{25: [{name: 'Alice', age: 25}, {name: 'Bob', age: 25}], 30: [{name: 'Charlie', age: 30}]}
  • Write a function that returns true if all the objects have a property named age that is greater than 18. The function should return false as soon as it finds the first object that does not satisfy this condition, without checking the remaining objects in the array.

Q2

Write a function that takes an array of arrays, and returns the Cartesian product of those arrays. The Cartesian product of two arrays is the set of all possible pairs where the first element comes from the first array, and the second element comes from the second array. For example, given the input [[1, 2], [3, 4]], the function should return [[1, 3], [1, 4], [2, 3], [2, 4]].

Q3

Write a function that takes two arrays of objects, and returns an array of objects that represent the "join" between the two arrays. The join between two objects is an object that contains the keys present in both objects, and the values are the values of those keys from both objects. For example, given the input arrays [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}] and [{id: 1, age: 25}, {id: 3, age: 30}], the function should return [{id: 1, name: 'Alice', age: 25}].

Q4

Implement a function that performs binary search on an array of numbers. The function should take in a sorted array of integers and a target integer to find. It returns the index of the target element or -1, if the target element doesn't exist in the array.

Binary search is a search algorithm that can efficiently determine if a sorted array of integers contain a specific number. The algorithm repeatedly divides the input array into half until the target element is found, thereby decreasing the search space by half each step. It is a significant improvement versus linear search.

Here is a quick explanation of how binary search works on an array that is already sorted:

  1. Calculate the middle index of the array and retrieve the middle element.
  2. If the target element is greater than the middle element, search the right half of the array (ignore the left half).
  3. If the target element is lesser than the middle element, search the left half of the array.
  4. If the target element is equal to the middle element, return the index of the element.
  5. Repeat the steps above until we complete the search. Return -1 if the target was not found.

Asynchronous Programming

Rewrite the following function using Promises instead of callbacks:

function fetchData(callback) {
  setTimeout(() => {
    callback({ data: "Hello, World!" });
  }, 1000);
}

Refactor the code below using async/await and arrow functions where appropriate, to make it more concise and easier to read.

function getUserData() {
  return new Promise(function(resolve, reject) {
    fetch('https://api.github.com/users/midasXIV')
      .then(response => response.json())
      .then(data => {
        resolve(data);
      })
      .catch(error => {
        reject(error);
      });
  });
}

Implement a function that accepts two promises and returns a single Promise. This returned promise fulfills when both input promises fulfill, with a single value according to the order and types of the fulfillment values:

  • Numbers should be added together.
  • Strings should be concatenated.
  • Arrays should be combined into a single array.
  • Plain objects should be merged into a single object.
  • Other types aren't supported.

The return promise can also be rejected if one of the following happens:

  • The types of the fulfilled results do not match, reject with the string 'Unsupported data types'.
  • One of the promises fail, reject with the rejected promise's reason.
Solution
await promiseMerge(Promise.resolve(1), Promise.resolve(2)); // 3
await promiseMerge(Promise.resolve('abc'), Promise.resolve('def')); // 'abcdef'
await promiseMerge(Promise.resolve([1, 2, 3]), Promise.resolve([4, 5, 6])); // [1, 2, 3, 4, 5, 6]
await promiseMerge(Promise.resolve({ foo: 1 }), Promise.resolve({ bar: 2 })); // { foo: 1, bar: 2}

 

await promiseMerge(Promise.resolve(1), Promise.resolve([])); // Rejected with 'Unsupported data types'
await promiseMerge(Promise.reject(1), Promise.resolve(2)); // Rejected with 1

Closures & Hoisting

Question 1

var variable = 10;
(()=>{
   console.log(variable);
   variable = 20;
   console.log(variable);
})();

console.log(variable);
var variable = 30;

Question 2

function createFunctions() {
  let result = [];
  for (let i = 0; i < 5; i++) {
    result[i] = function() {
      console.log(i);
    };
  }
  return result;
}

let fnArr = createFunctions();
fnArr[0]();
fnArr[1]();
fnArr[2]();

Question 3

function foo() {
  let arr = [];
  for (var i = 0; i < 5; i++) {
    arr.push(function() {
      console.log(i);
    });
  }
  return arr;
}

let arr = foo();
arr[0]();
arr[1]();
arr[2]();

Question 4

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 0);
}

Question 5: What is the output of the following code, and why?

console.log(x);
var x = 10;

------------------------

console.log(x);
let x = 10;

Question 6:

let x = 10;
function bar() {
  console.log(x);
  let x = 5;
}
bar();

--------------------------

var x = 10;
function foo() {
  console.log(x);
  var x = 5;
}
foo();

Question 7

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}
}
b();
console.log(a);

Destructuring

Question 1

Write a function that takes an object as a parameter and uses destructuring to extract the properties "name" and "age" from the object.

function displayUser(user) {
  console.log(`Name: ${user.name}, Age: ${user.age}`);
}

const user = { name: "John", age: 30 };
displayUser(user); // "Name: John, Age: 30"

Question2

Use destructuring to extract the first, second and a variable contaning rest of elements from the array: .

const numbers = [10, 20, 30, 40, 50];


console.log(first); // 10
console.log(second); // 20
console.log(rest); // [30, 40, 50]

Question 3

Given the following object:

const user = { name: "John", age: 30, address: { city: "New York", state: "NY" } };

console.log(name); // "John"
console.log(city); // "New York"
console.log(state); // "NY"

use destructuring to extract the properties "name" and "city" from the object and assign them to the variables personName and personCity.

Question 4

For the following object, using destructuring extract the primary and hidden property under "ability" and store them in variables like primaryAbility, hiddenAbility.

let pokemon = {
    "name": "Pikachu",
    "id": 25,
    "type": "electric",
    "ability": {
        "primary": "Static",
        "hidden": "Lightning rod"
    },
    "moves": ["Quick Attack", "Volt Tackle", "Iron Tail", "Thunderbolt"]
};

Q1: Refactor the following code using best practices:

function getUserDetails(user) {
  if (user) {
    if (user.name) {
      console.log('User Name: ' + user.name);
    } else {
      console.log('No name provided');
    }
    if (user.age) {
      console.log('User Age: ' + user.age);
    } else {
      console.log('No age provided');
    }
  } else {
    console.log('No user object provided');
  }
}

Q2: Refactor the following code to use the reduce method instead of a for loop:

function sumArray(arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum;
}

Q3

function mul (x) {
  return function (y) { // anonymous function
    return function (z) { // anonymous function
      return x * y * z;
    };
  };
}

Q4

debouncing is a technique used to control how many times we allow a function to be executed over time. When a JavaScript function is debounced with a wait time of X milliseconds, it must wait until after X milliseconds have elapsed since the debounced function was last called. You almost certainly have encountered debouncing in your daily lives before — when entering an elevator. Only after X duration of not pressing the "Door open" button (the debounced function not being called) will the elevator door actually close (the callback function is executed).

Implement a debounce function which accepts a callback function and a wait duration. Calling debounce() returns a function which has debounced invocations of the callback function following the behavior described above.

Solution
let i = 0;
function increment() {
  i++;
}
const debouncedIncrement = debounce(increment, 100);

 

// t = 0: Call debouncedIncrement().
debouncedIncrement(); // i = 0

 

// t = 50: i is still 0 because 100ms have not passed.
//  Call debouncedIncrement() again.
debouncedIncrement(); // i = 0

 

// t = 100: i is still 0 because it has only
//  been 50ms since the last debouncedIncrement() at t = 50.

 

// t = 150: Because 100ms have passed since
//  the last debouncedIncrement() at t = 50,
//  increment was invoked and i is now 1 .

Q5

Build a tabs component that displays one panel of content at a time depending on the active tab element. Some HTML is provided for you as example contents.

Requirements Clicking on a tab makes it the active tab. Add a visual indication (e.g. using blue text color) for the active tab to differentiate it from the non-active tabs. At all times, only one panel's contents should be displayed — the one corresponding to the active tab's. Notes The focus of this question is on functionality, not the styling. There's no need to write any custom CSS except for highlighting the active tab. You may modify the markup (e.g. adding ids, data attributes, replacing some tags, etc) and use client-side rendering instead. You may want to think about ways to improve the user experience of the application and implement them

Solution
export default function Tabs() {

  return (

    <div>

      <div>

        <button>HTML</button>

        <button>CSS</button>

        <button>JavaScript</button>

      </div>

      <div>

        <p>

          The HyperText Markup Language or HTML is the

          standard markup language for documents designed to

          be displayed in a web browser.

        </p>

        <p>

          Cascading Style Sheets is a style sheet language

          used for describing the presentation of a document

          written in a markup language such as HTML or XML.

        </p>

        <p>

          JavaScript, often abbreviated as JS, is a

          programming language that is one of the core

          technologies of the World Wide Web, alongside HTML

          and CSS.

        </p>

      </div>

    </div>

  );

}
import React, { useState } from 'react';

const Dashboard = () => {
  const [userDetails, setUserDetails] = useState({
    name: 'John Doe',
    email: 'john@example.com',
  });

  const [recentActivity, setRecentActivity] = useState(['Logged in', 'Viewed profile']);
  const [favoriteItems, setFavoriteItems] = useState(['Item A', 'Item B']);

  const [tasks, setTasks] = useState([
    { id: 1, title: 'Complete project', completed: false },
    { id: 2, title: 'Write documentation', completed: false },
    // ...more tasks
  ]);

  const toggleTaskCompletion = (taskId) => {
    setTasks((prevTasks) =>
      prevTasks.map((task) =>
        task.id === taskId ? { ...task, completed: !task.completed } : task
      )
    );
  };
  
  return (
    <div>
      <h1>Welcome, {userDetails.name}!</h1>
      <div>
        <h2>Recent Activity:</h2>
        <ul>
          {recentActivity.map((activity, index) => (
            <li key={index}>{activity}</li>
          ))}
        </ul>
      </div>
      <div>
        <h2>Favorite Items:</h2>
        <ul>
          {favoriteItems.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      </div>
      <div>
      <h2>Task List</h2>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>
            <input
              type="checkbox"
              checked={task.completed}
              onChange={() => toggleTaskCompletion(task.id)}
            />
            {task.title}
          </li>
        ))}
      </ul>
    </div>
    </div>
  );
};

export default Dashboard;

Typescript

Q1. What would be the type

type TypeName<T> = 
T extends string ? "string" 
: T extends number ? "number" 
: T extends boolean ? "boolean" 
: T extends undefined ? "undefined" 
: T extends Function ? "function" 
: "object"; 

type TO = TypeName<string>; 
type T1 = TypeName<"a">; 
type T2 = TypeName<string[]>; 

Question 2: UTility Types

Suppose you have an interface named Order that describes an online order, with the following interface:

interface Order {
  orderId: string;
  customerId: string;
  date: string;
  total: number;
  shippingAddress: {
    street: string;
    city: string;
    state: string;
    zip: string;
  };
  billingAddress: {
    street: string;
    city: string;
    state: string;
    zip: string;
  };
  items: Array<{
    productId: string;
    quantity: number;
    price: number;
  }>;
  status?: 'pending' | 'shipped' | 'delivered';
}
  1. You need to write a function that updates only the shippingAddress and billingAddress fields of an order, and returns the updated order object. However, you also need to ensure that any non-editable fields of the order are preserved in the updated object. How would you define the argument and return types for this function, and how would you implement the function to achieve this?
Solution
function updateOrder(order: Order, orderFieldsToUpdate: Partial<Order>): Order {
  const updatedOrder: Order = {
    ...order, // Preserve any non-editable fields
    ...orderFieldsToUpdate, // Update only the specified fields
  };

  return updatedOrder;
}
  1. Write a function that takes a user object as an argument, and returns a new object with only the orderId, items and status fields from the original object. How would you define the argument and return types for this function, and how would you implement the function to achieve this?
Solution
function pickOrderFields(order: Order): Pick<Order, "shippingAddress" | "billingAddress"> {
  
  return {
  shippingAddress: order.shippingAddress,
  billingAddress: order.billingAddress
  };
}
  1. Write a function that takes a order object as an argument, and returns a new object with all fields from the original object except for the shipping fields. How would you define the argument and return types for this function, and how would you implement the function to achieve this?
Solution
function omitOrderShippingFields(order: Order): Omit<Order, "orderId" | "items" | "status"> {
  const pickedFields = {
    orderId: order.orderId,
    items: order.items,
    status: order.status
  };
  
  return pickedFields;
}
  1. Can you explain how using Utility Types in this way can make your code more robust and maintainable, compared to simply defining separate types for editable and non-editable fields?

Question 4

Given the following function signature:

type UnaryFn<T, R> = (arg: T) => R;
type ChainFn<T, U, R> = (fn1: UnaryFn<T, U>, fn2: UnaryFn<U, R>) => UnaryFn<T, R>;

function chain<T, U, R>(fn1: UnaryFn<T, U>, fn2: UnaryFn<U, R>): UnaryFn<T, R> {
  return (arg: T) => fn2(fn1(arg));
}

Implement a compose function using the chain function that composes any number of unary functions into a single function that can be called with an argument of the first function's input type and produces a result of the last function's output type. What would the function signature look like ?

The compose function should take any number of unary functions as arguments, and should return a new function that applies all of the input functions in sequence from right to left. For example, given the following functions:

function addOne(x: number): number {
return x + 1;
}

function double(x: number): number {
return x * 2;
}

function square(x: number): number {
return x * x;
}

The compose function should work like this:

const fn = compose(addOne, double, square); // fn: UnaryFn<number, number>
const result = fn(3); // result: 20 (3 => square => double => addOne)
Solution
function compose<T, R>(...fns: Array<UnaryFn<unknown, unknown>>): UnaryFn<T, R> {
return (arg: T) => fns.reduce((prevResult, fn) => chain(fn)(prevResult), arg) as R;
}

Question 3

Implement a type-safe version of the Object.keys function in TypeScript. How would the function signature look like ?

The typedKeys function should take an object as its argument, and should return an array of the object's keys as strings. However, the function should be type-safe in that the returned array should be typed such that it only includes keys that are actually present on the input object.

For example, given the following object:

const person = {
  name: 'John',
  age: 30,
  email: 'john@example.com'
};

The typedKeys function should work like this:

const keys = typedKeys(person); // keys: Array<"name" | "age" | "email">
Solution
function typedKeys<T extends object>(obj: T): Array<keyof T> {
  // implementation
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment