jsnotes

Topic 010: Coding Questions in JS

Topic 010: Coding Questions in JS

Table of Contents

Sr.No. Question
0 Get h2 tag and links using JS for github io
1 How can I ensure a JavaScript function scheduled with setTimeout for 2000 ms executes before another function scheduled for 1000 ms using async/await and Promises? Can you provide a different approach using async/await that doesn’t involve a separate wait function?
2 Write a JavaScript program to count the occurrences of each element in an array and display the counts.
3 Callback hell and the Solution.
4 Create a function that takes an array of promises and returns a promise that resolves after running those promises in series.
5 Implementation of an Event Emitter in JavaScript
6 Count the occurrences of each character from string and return a object, after that copy object, remove space key from it and club string together
7 How can I extract values from a JavaScript object and display them grouped by department using for, forEach and hasOwnProperty?
8 Code and Output
9 Generate URL with different language code and handle special scenarios.
10 Unlike twitter likes
var ghead = document.getElementsByTagName("h2");

for (i = 0; i < ghead.length; i++) {
  var gh = ghead[i];
  var ts = gh.id;
  var ts1 = gh.textContent;
  console.log(ts1);
  console.log(ts);
  //console.log(i);
}

1. How can I ensure a JavaScript function scheduled with setTimeout for 2000 ms executes before another function scheduled for 1000 ms using async/await and Promises? Can you provide a different approach using async/await that doesn’t involve a separate wait function?

Using Callbacks

You can nest the setTimeout functions to ensure the order:

function function2000ms(callback) {
  setTimeout(() => {
    console.log("This function is executed after 2000 ms");
    callback();
  }, 2000);
}

function function1000ms() {
  setTimeout(() => {
    console.log("This function is executed after 1000 ms");
  }, 1000);
}

// Execute function2000ms and pass function1000ms as the callback
function2000ms(function1000ms);

Using Promises

You can use Promises to chain the execution:

function function2000ms() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("This function is executed after 2000 ms");
      resolve();
    }, 2000);
  });
}

function function1000ms() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("This function is executed after 1000 ms");
      resolve();
    }, 1000);
  });
}

// Chain the Promises to control the order
function2000ms().then(function1000ms);

Using async/await

The async/await syntax makes it even clearer and more readable:

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function executeFunctions() {
  await wait(2000);
  console.log("This function is executed after 2000 ms");
  await wait(1000);
  console.log("This function is executed after 1000 ms");
}

// Execute the async function
executeFunctions();

In this example:

Using async/await with Promises

You can create a utility function to handle setTimeout with a Promise and then use async/await to control the execution order.

// Utility function that returns a Promise resolving after the specified time
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function function2000ms() {
  await delay(2000);
  console.log("This function is executed after 2000 ms");
}

async function function1000ms() {
  await delay(1000);
  console.log("This function is executed after 1000 ms");
}

async function executeFunctions() {
  await function2000ms(); // Wait for the function2000ms to complete
  await function1000ms(); // Then wait for the function1000ms to complete
}

// Execute the async function
executeFunctions();

Explanation

  1. Utility Function delay: This function returns a Promise that resolves after a specified number of milliseconds using setTimeout.

  2. Async Functions function2000ms and function1000ms: These are asynchronous functions that wait for the delay to complete before executing their code.

  3. Main Execution Function executeFunctions: This function uses await to ensure function2000ms completes before starting function1000ms.

2. Write a JavaScript program to count the occurrences of each element in an array and display the counts.

// Input array
let array = [10, 20, 30, 20, 30, 20];

// Create an object to store the counts of each element
let counts = {};

// Iterate through the array
for (let i = 0; i < array.length; i++) {
  let num = array[i];
  // If the element is already in the object, increment its count
  if (counts[num]) {
    counts[num]++;
  } else {
    // If the element is not in the object, add it with a count of 1
    counts[num] = 1;
  }
}

// Output the counts
for (let num in counts) {
  console.log(`Count of ${num} is ${counts[num]}`);
}

When you run this script, you should get the following output:

Count of 10 is 1
Count of 20 is 3
Count of 30 is 2

2nd Method :

function countOccurrences(array) {
  return array.reduce((acc, element) => {
    if (acc[element]) {
      acc[element] += 1;
    } else {
      acc[element] = 1;
    }
    return acc;
  }, {});
}

// Example usage:
const array = ["apple", "banana", "apple", "orange", "banana", "apple"];
const counts = countOccurrences(array);
console.log(counts);
// Output: { apple: 3, banana: 2, orange: 1 }

3rd Method :

function countOccurrences(array) {
  return array.reduce((acc, element) => {
    acc[element] = (acc[element] || 0) + 1;
    return acc;
  }, {});
}

// Example usage:
const array = ["apple", "banana", "apple", "orange", "banana", "apple"];
const counts = countOccurrences(array);
console.log(counts);
// Output: { apple: 3, banana: 2, orange: 1 }

3. Callback hell and the Solution.

Callback hell refers to the situation where you have multiple nested callbacks within asynchronous JavaScript code, leading to code that is hard to read, understand, and maintain. This typically occurs when you have asynchronous operations dependent on the results of other asynchronous operations, resulting in deeply nested callback functions.

Here’s an example of what callback hell might look like:

asyncOperation1((result1) => {
  asyncOperation2(result1, (result2) => {
    asyncOperation3(result2, (result3) => {
      // More nested callbacks...
    });
  });
});

Asynchronous operations in JavaScript, like fetching data from a server, reading from a file, or waiting for a user interaction, often involve callbacks. When you have multiple asynchronous operations depending on each other, each requiring a callback, the code can become difficult to manage due to the deeply nested structure.

There are several solutions to mitigate callback hell:

  1. Use Named Functions: Define functions outside of the callback chain and refer to them by name within the callbacks. This makes the code more readable and easier to understand.
function handleResult1(result1) {
  asyncOperation2(result1, handleResult2);
}

function handleResult2(result2) {
  asyncOperation3(result2, handleResult3);
}

asyncOperation1(handleResult1);
  1. Use Promises: Promises provide a cleaner way to handle asynchronous operations and avoid callback hell. Promises allow you to chain asynchronous operations sequentially, making the code more readable and maintainable.
asyncOperation1()
  .then((result1) => asyncOperation2(result1))
  .then((result2) => asyncOperation3(result2))
  .then((result3) => {
    // Handle final result
  })
  .catch((error) => {
    // Handle errors
  });
  1. Use Async/Await: Async/await is a modern JavaScript feature that allows you to write asynchronous code in a synchronous style. It’s built on top of promises and provides a cleaner syntax for handling asynchronous operations.
async function fetchData() {
  try {
    const result1 = await asyncOperation1();
    const result2 = await asyncOperation2(result1);
    const result3 = await asyncOperation3(result2);
    // Handle final result
  } catch (error) {
    // Handle errors
  }
}

fetchData();

4. Create a function that takes an array of promises and returns a promise that resolves after running those promises in series.

function runPromisesInSeries(promises) {
  return promises.reduce((accumulator, currentPromise) => {
    return accumulator.then(currentPromise);
  }, Promise.resolve());
}

// Example usage:

// Dummy functions returning promises for demonstration
const promise1 = () =>
  new Promise((resolve) => {
    setTimeout(() => {
      console.log("Promise 1 resolved");
      resolve("Result 1");
    }, 1000);
  });

const promise2 = () =>
  new Promise((resolve) => {
    setTimeout(() => {
      console.log("Promise 2 resolved");
      resolve("Result 2");
    }, 1000);
  });

const promise3 = () =>
  new Promise((resolve) => {
    setTimeout(() => {
      console.log("Promise 3 resolved");
      resolve("Result 3");
    }, 1000);
  });

runPromisesInSeries([promise1, promise2, promise3]).then(() => {
  console.log("All promises completed");
});

5. Implementation of an Event Emitter in JavaScript

The Event Emitter will have the following methods:

Implementation

Here is the implementation of the Event Emitter in JavaScript:

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

  // Registers a listener for a specific event
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  // Unregisters a listener for a specific event
  off(event, listener) {
    if (!this.events[event]) return;

    this.events[event] = this.events[event].filter((l) => l !== listener);
  }

  // Emits an event, calling all registered listeners with the provided arguments
  emit(event, ...args) {
    if (!this.events[event]) return;

    this.events[event].forEach((listener) => listener(...args));
  }

  // Registers a listener for a specific event that will be called at most once
  once(event, listener) {
    const onceListener = (...args) => {
      listener(...args);
      this.off(event, onceListener);
    };
    this.on(event, onceListener);
  }
}

// Usage example
const emitter = new EventEmitter();

const onFoo = (message) => {
  console.log("foo listener:", message);
};

emitter.on("foo", onFoo);
emitter.emit("foo", "Hello, World!"); // Outputs: foo listener: Hello, World!

emitter.off("foo", onFoo);
emitter.emit("foo", "This will not be logged"); // No output

emitter.once("bar", (message) => {
  console.log("bar listener:", message);
});
emitter.emit("bar", "This will be logged once"); // Outputs: bar listener: This will be logged once
emitter.emit("bar", "This will not be logged"); // No output

Explanation

  1. Constructor: Initializes the events object which will hold event names as keys and arrays of listener functions as values.
  2. on Method: Adds a listener to the array of listeners for a specified event. If the event does not exist, it initializes it with an empty array first.
  3. off Method: Removes a specific listener from the array of listeners for a specified event. If the event or the listener does not exist, it does nothing.
  4. emit Method: Calls all the listeners registered for a specific event with the provided arguments.
  5. once Method: Adds a listener that will be automatically removed after it is called once.

6. Count the occurrences of each character from string and return a object, after that copy object, remove space key from it and club string together.

1st Method :

let str = "I love paypal";

// Split the string into an array of characters
let arrChars = str.split("");

// Count the occurrence of each character
const arrCharOccurrence = arrChars.reduce((prev, char) => {
  if (prev.hasOwnProperty(char)) {
    prev[char] = prev[char] + 1;
  } else {
    prev[char] = 1;
  }
  return prev;
}, {});

console.log(arrCharOccurrence); //{ I: 1, ' ': 2, l: 2, o: 1, v: 1, e: 1, p: 2, a: 2, y: 1 }

// Deep opy the object
const copiedObject = JSON.parse(JSON.stringify(arrCharOccurrence));

// Remove spaces from the copied object
delete copiedObject[" "];

console.log(copiedObject); // { I: 1, l: 2, o: 1, v: 1, e: 1, p: 2, a: 2, y: 1 }

// Club string together (concatenate characters)
const concatenatedString = Object.keys(copiedObject).join("");

console.log(concatenatedString); // Output: "Ilovepay"

2nd Method :

function countOccurrencesAndModifyString(input) {
  // Step 1: Count occurrences of each character
  const charCount = {};
  for (const char of input) {
    if (charCount[char]) {
      charCount[char]++;
    } else {
      charCount[char] = 1;
    }
  }

  // Step 2: Remove space key from the object
  const charCountCopy = { ...charCount };
  delete charCountCopy[" "];

  // Step 3: Combine the characters of the string, excluding spaces
  const modifiedString = input.replace(/\s+/g, "");

  return {
    charCount: charCount,
    modifiedString: modifiedString,
  };
}

// Example usage:
const inputString = "I love paypal";
const result = countOccurrencesAndModifyString(inputString);
console.log("Character Count:", result.charCount);
console.log("Modified String:", result.modifiedString); // Should print "Ilovepaypal"

// For the expected output "ilovepay":
const lowercaseInput = inputString.toLowerCase();
const finalResult = countOccurrencesAndModifyString(lowercaseInput);
console.log("Character Count (Lowercase):", finalResult.charCount);
console.log("Modified String (Lowercase):", finalResult.modifiedString); // Should print "ilovepaypal"

7. How can I extract values from a JavaScript object and display them grouped by department using for, forEach and hasOwnProperty?

Data :

const employees = {
  Alice: {
    code: "A3443",
    department: "Sales",
  },
  Bob: {
    code: "B34s43",
    department: "Marketing",
  },
  Charlie: {
    code: "C3444",
    department: "HR",
  },
  David: {
    code: "D34s44",
    department: "Engineering",
  },
  Eva: {
    code: "E3445",
    department: "Sales",
  },
  Frank: {
    code: "F34s45",
    department: "Marketing",
  },
  Grace: {
    code: "G3446",
    department: "HR",
  },
  Hannah: {
    code: "H34s46",
    department: "Engineering",
  },
};

1st Method :

const departments = {};

Object.entries(employees).forEach(([name, details]) => {
  const { department } = details;
  if (!departments.hasOwnProperty(department)) {
    departments[department] = [];
  }
  departments[department].push(name);
});

console.log(departments);

2nd Method :

const departments = {};

for (const [name, details] of Object.entries(employees)) {
  const { department } = details;
  if (!departments[department]) {
    departments[department] = [];
  }
  departments[department].push(name);
}

console.log(departments);

3rd Method :

function departMentWiseUsers(obj) {
  const arrUsers = Object.entries(obj);

  //console.log(JSON.stringify(Object.entries(obj)));

  let department = {};

  arrUsers.forEach((user, index) => {
    // console.log(user[1]);

    if (department.hasOwnProperty(user[1].department)) {
      department[user[1].department].push(user[0]);
    } else {
      department[user[1].department] = [user[0]];
    }
  });

  return department;
}
console.log(JSON.stringify(departMentWiseUsers(employees)));

Note : if in Data the value is 1 level under(i.e., const employees = { "input" : {"name":...}} ) to access or pass that data useemployees.input

8. Code and Output

function test(num) {
  const add = () => {
    return num + 12;
  };
  return add;
}

const addition = test(23);
const addition1 = test(10);
const addition2 = test(14);

console.log(addition()); // Output: 35
console.log(addition1()); // Output: 22
console.log(addition2()); // Output: 26

9. Generate URL with different language code and handle special scenarios.

function generateLink(langCode) {
  const specialCases = {
    "pt-BR": "br/pt",
    "fr-CA": "ca/fr",
    "fr-FR": "fr/fr",
    "zh-CN": "cn/zh",
    "zn-TW": "tw/zn",
    cs: "cz/cs",
    da: "dk/da",
    es: "es/es",
    et: "ee/et",
    he: "il/he",
    ja: "jp/ja",
    ko: "kr/ko",
    pt: "pt/pt",
    sl: "si/sl",
    sv: "se/sv",
    uk: "ua/uk",
    vi: "vn/vi",
  };

  // Handle the default case
  let path;
  if (specialCases.hasOwnProperty(langCode)) {
    path = specialCases[langCode];
  } else {
    path = `${langCode}/${langCode}`;
  }

  // Return the complete URL
  return `https://www.xyz.com/${path}/Home.page`;
}

// Test the function
const langCodes = [
  "bg",
  "ar",
  "cs",
  "da",
  "de",
  "et",
  "fi",
  "fr-CA",
  "fr-FR",
  "he",
  "hu",
  "it",
  "ja",
  "ko",
  "lt",
  "lv",
  "no",
  "pl",
  "pt-BR",
  "pt-PT",
  "ro",
  "ru",
  "sk",
  "sl",
  "sv",
  "th",
  "tr",
  "uk",
  "vi",
  "zh-CN",
  "zn-TW",
];
langCodes.forEach((code) => {
  console.log(`Lang code: ${code}, Link: ${generateLink(code)}`);
});

10. Unlike twitter likes.

function nextUnlike() {
  return document.querySelector('[data-testid="unlike"]');
}

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function removeAll() {
  let count = 0;
  let next = nextUnlike();
  let waitTime = 3000; // Start with a shorter wait time of 3 seconds

  while (next && count < 1200) {
    try {
      next.focus();
      next.click();
      console.log(`Unliked ${++count} tweets`);
      await wait(waitTime); // Initial wait time of 3 seconds
      next = nextUnlike();

      // If no unlike button is found, scroll to load more
      if (!next && count < 1200) {
        window.scrollTo(0, document.body.scrollHeight);
        await wait(5000); // Shorter wait for loading more tweets
        next = nextUnlike();
      }
    } catch (error) {
      console.error("An error occurred:", error);
      waitTime = Math.min(waitTime * 2, 60000); // Exponentially increase wait time if an error occurs
      console.log(`Rate limit hit? Increasing wait time to ${waitTime / 1000} seconds.`);
      await wait(waitTime); // Wait before retrying
    }
  }

  if (next) {
    console.log("Finished early to prevent rate-limiting");
  } else {
    console.log("Finished, count =", count);
  }
}

removeAll();