Algorithms
Recursive Summation
function calculateArraySum(arr, index) {
return index < 0 ? 0 : arr[index] + calculateArraySum(arr, index - 1);
}
JavaScript Techniques
Array Operations
Find Array Intersection
function findCommonElements(array1, array2) {
return array1.filter(element => array2.includes(element));
}
Calculate Array Sum
function getArraySum(array) {
return array.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
}
Clear an Array
let sampleArray = [1, 2, 3, 4, 5];
// Method 1: Reassign to empty array
sampleArray = [];
// Method 2: Use splice method
sampleArray.splice(0);
// Method 3: Set length property to 0
sampleArray.length = 0;
ES6 Features
Arrow Functions and 'this' Context
const userObject = {
name: 'John Doe',
greetWithArrow: () => {
console.log(this.name);
},
greetWithMethod() {
(() => {
console.log(this.name);
})();
}
};
userObject.greetWithArrow(); // Output: undefined (points to global object)
userObject.greetWithMethod(); // Output: John Doe
Code Examples
Truthy and Falsy Values in Loops
let counter = 0;
const numbers = [0, 1, 2, 3];
numbers.forEach(number => {
if (number) counter += 1;
});
console.log(counter); // Output: 3 (only 1, 2, 3 are truthy)
Parameter Passing and Data Types
function modifyParameters(object, value) {
// Modifying the object property affects the original object
object.name = 'Modified';
// Value is a primitive type, so this only changes the local variable
value = 'Modified';
}
const person = { name: 'Original' };
const number = 42;
modifyParameters(person, number);
console.log(person, number); // Output: { name: 'Modified' } 42
Object References
let objectA = { value: 1, count: 2 };
let objectB = objectA; // Both point to the same object
objectB = { value: 3, count: 4 }; // Now objectB points to a new object
console.log(objectA.value, objectA.count, objectB.value, objectB.count); // Output: 1 2 3 4
let objectC = { value: 1, count: 2 };
let objectD = objectC; // Both point to the same object
objectD.value = 3; // Modifies the same object
objectD.count = 4; // Modifies the same object
console.log(objectC.value, objectC.count, objectD.value, objectD.count); // Output: 3 4 3 4
Object Reference Copying
let person = { name: "Alex" };
const group = [person];
person = null;
console.log(group); // Output: [{ name: "Alex" }] because group still references the object
JSON Serialization
const dataObject = {
a: 3,
b: 4,
c: null,
d: undefined,
get e() {
return 5;
}
};
console.log(JSON.stringify(dataObject));
// Output: {"a":3,"b":4,"c":null,"e":5}
Scope and Variable Hoisting
var globalVar = 0;
function firstFunction() {
console.log(globalVar); // 0 (accesses global variable)
console.log(this.globalVar); // 0 (non-strict mode, this points to global object)
globalVar = 1; // Modifies global variable
console.log(globalVar); // 1
console.log(this.globalVar); // 1
}
function secondFunction() {
console.log(globalVar); // undefined (hoisting of declaration)
console.log(this.globalVar); // 1 (still accessing global)
var globalVar = 2; // Creates a new variable in this scope
console.log(globalVar); // 2
console.log(this.globalVar); // 1 (still accessing global)
}
firstFunction();
secondFunction();
Function Chaining
const result = console.log.call.call.call.call.call.call.apply(
(x) => x,
[1, 2]
);
console.log(result); // Output: 2
'this' Context in Different Scenarios
let length = 1;
function displayLength() {
console.log(this.length);
}
let array = [displayLength, 'a', 'b'];
array[0](); // Output: 3 (this points to array)
let standaloneFunction = array[0];
standaloneFunction(); // Output: 1 (this points to window/global object)
let name = 'Global Name';
let userObject = {
name: 'Local Name',
sayName: function() {
console.log(this.name);
}
};
userObject.sayName(); // Output: Local Name
setTimeout(userObject.sayName, 1000); // Output: Global Name (loses context)
setTimeout(() => userObject.sayName(), 1000); // Output: Local Name (arrow function preserves context)
setTimeout(userObject.sayName.bind(userObject), 1000); // Output: Local Name (bind preserves context)
'this' Context with Arrow Functions
const shape = {
radius: 10,
diameter() { return this.radius * 2 },
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter(); // Output: 20
shape.perimeter(); // Output: NaN (arrow function inherits 'this' from enclosing scope)
Object Property Name Types
const array = [1, 2];
array[0]++;
array["1"]++;
console.log(array[0], array["1"]); // Output: 2 3
// Array becomes: {"0": 2, "1": 3, length: 2}
Type Conversion and Operator Precedence
console.log(3 > 2 > 1); // Output: false (3>2 evaluates to true, then true>1 evaluates to false)
console.log(3 < 2 < 1); // Output: true (3<2 evaluates to false, then false<1 evaluates to true)</code>
Closures and Event Loop
for (var i = 0; i < 3; i++) {
console.log(i);
setTimeout(() => console.log(i), i * 1000);
}
// Output: 0 1 2 immediately, then 3 3 3 after 0s, 1s, 2s respectively
// setTimeout is asynchronous, so when it executes, i has already become 3
Interview Questions
Array Grouping Utility
const people = [
{ name: 'Alice', age: 30, gender: 'female' },
{ name: 'Bob', age: 25, gender: 'male' },
{ name: 'Charlie', age: 30, gender: 'male' },
{ name: 'Diana', age: 25, gender: 'female' },
{ name: 'Eva', age: 25, gender: 'female' },
{ name: 'Frank', age: 25, gender: 'male' },
{ name: 'Grace', age: 20, gender: 'female' }
];
function groupBy(array, keyGenerator) {
if (typeof keyGenerator === 'string') {
const propertyName = keyGenerator;
keyGenerator = (item) => item[propertyName];
}
const result = {};
for (const item of array) {
const key = keyGenerator(item);
if (!result[key]) {
result[key] = [];
}
result[key].push(item);
}
return result;
}
console.debug(groupBy(people, "age"));
console.debug(groupBy(people, (item) => `${item.age}-${item.gender}`));
Dynamic JavaScript Execution
|
Variable Scope |
Synchronous/Asynchronous |
| eval() |
Current |
Synchronous |
| setTimeout() |
Global |
Asynchronous |
| Function |
Global |
Synchronous |
| script |
Global |
Synchronous |
let variable = 1;
function executeCode(code) {
let localVariable = 2;
eval(code) // variable:2, synchronous (current scope)
setTimeout(code, 0) // variable:1, asynchronous (global scope)
const dynamicFunction = new Function(code)
dynamicFunction() // variable:1, synchronous (global scope)
const scriptElement = document.createElement("script")
scriptElement.innerHTML = code
document.body.appendChild(scriptElement) // variable:1, synchronous (global scope)
}
executeCode('console.log("variable:",variable)')
console.log(`%csynchronous`, 'font-weight: bold; color: white; background: black;');
Destructuring Assignment
const dataObject = {
// name: 'demo',
age: 20,
// gender: "male"
}
let { name = "default", age: ageValue, gender: genderValue = "unknown" } = dataObject;
console.debug(name); // Output: default
console.debug(ageValue); // Output: 20
console.debug(genderValue); // Output: unknown
Scroll to Element
// Smooth scroll to element center
element.scrollIntoView({behavior:'smooth',block:'center'})
Manual DOM Parsing
function extractTextFromHTML(htmlString) {
return (
new DOMParser().parseFromString(htmlString, "text/html").body.textContent ||
""
);
}
console.log(
extractTextFromHTML(`
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>
<ul>
<li>Apple</li>
<li>Pear</li>
<li>Banana</li>
</ul>
</div>
</body>
</html>
`)
);
/* Apple
Pear
Banana */
Algorithm Challenges
Large Integer Addition
/**
* Adds two large numbers represented as strings
* @param {string} numA
* @param {string} numB
* @return {string} Result
* @example
* addLargeNumbers('99', '1') => '100'
*/
function addLargeNumbers(numA, numB) {
// Determine the length of the longer number
const maxLength = Math.max(numA.length, numB.length);
// Pad the shorter number with zeros to match the length of the longer number
numA = numA.padStart(maxLength, '0');
numB = numB.padStart(maxLength, '0');
// Initialize carry as 0
let carry = 0;
// Store the result
let result = '';
// Start from the end and add digit by digit
for (let i = maxLength - 1; i >= 0; i--) {
// Add current digits and carry
const sum = +numA[i] + +numB[i] + carry;
// Calculate current digit
const digit = sum % 10;
// Update carry
carry = Math.floor(sum / 10);
// Add current digit to the beginning of result
result = digit + result;
}
// If there's a carry left, add it to the beginning
if (carry) {
result = carry + result;
}
// Return final result
return result;
}
Promise Implementation
// Given an asynchronous function with callback
function getDataWithCallback(a, b, callback) {
setTimeout(function () {
callback(a + b);
}, 1000);
}
// Implement a new function that returns a Promise
function getDataWithPromise(a, b) {
return new Promise(function (resolve, reject) {
getDataWithCallback(a, b, function (result) {
resolve(result);
});
});
}
// Usage
getDataWithPromise(1, 2).then((result) => {
console.log(result);
});
Type Conversion Examples
// Values that convert to false in boolean context
- Number 0
- NaN
- Empty string ""
- null / undefined
console.log([] == 0); // true - empty array converts to empty string, then to 0
console.log([] == false); // true - [] converts to empty string, then to 0, then to false
console.log(![] == false); // true - [] is truthy, ![] becomes false
console.log(null == undefined); // true
console.log(null == 0); // false - null/undefined don't equal any other value
console.log(undefined == 0); // false - null/undefined don't equal any other value
console.log("[object Object]" == {}); // true - object converts to string
console.log(Number("abc") == Number("aaa")); // false - NaN == NaN is false
URL Parsing
// URL to parse
const url = "https://www.example.com?search=test&page=1&limit=10";
// Expected output object
const parsedData = {
protocol: "https",
host: "example.com",
origin: "https://www.example.com",
query: {
search: "test",
page: 1,
limit: 10,
},
};
// Implementation
const urlObject = new URL(url);
const result = {
protocol: urlObject.protocol.replace(":", ""),
host: urlObject.hostname,
origin: urlObject.origin,
query: {},
};
urlObject.searchParams.forEach((value, key) => {
if (!isNaN(value)) {
value = parseInt(value);
}
result.query[key] = value;
});
console.log(result);
// Function to update data when URL changes
function updateDataFromURL(newURL) {
const newURLObject = new URL(newURL);
result.protocol = newURLObject.protocol.replace(":", "");
result.host = newURLObject.hostname;
result.origin = newURLObject.origin;
result.query = {};
newURLObject.searchParams.forEach((value, key) => {
if (!isNaN(value)) {
value = parseInt(value);
}
result.query[key] = value;
});
}
// Example of URL change
updateDataFromURL("https://newsite.com?query=demo&page=2&limit=20");
Check for Empty Object
function isEmptyObject(obj) {
return (typeof obj === 'object') && (obj !== null) && (Object.keys(obj).length === 0);
}
Parameter Passing in Functions
let user = {
name: "Alex",
};
function modifyUser(user) {
user.name = "Bob";
user = {
name: "Charlie",
};
console.log("Internal user name:", user.name);
}
modifyUser(user);
console.log("External user name:", user.name);
/* Internal user name: Charlie
External user name: Bob */
Event Loop and Closures
for (var i = 0; i < 5000; i += 1000) {
setTimeout(function () {
console.log(i);
}, i);
}
// Output: 5 printed 5 times after 0s, 1s, 2s, 3s, 4s respectively
// When setTimeout executes, i has already become 5
Event Loop: Main Thread, Microtasks, Macrotasks
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
new Promise((resolve) => {
console.log(4);
resolve(5);
}).then((data) => {
console.log(data);
Promise.resolve()
.then(() => {
console.log(6);
})
.then(() => {
console.log(7);
setTimeout(() => {
console.log(8);
}, 0);
});
});
setTimeout(() => {
console.log(9);
});
console.log(10);
process.nextTick(function () {
console.log(11);
});
async function async1() {
console.log(12);
await async2();
console.log(13);
}
async function async2() {
console.log(14);
await async3();
console.log(15);
}
async function async3() {
console.log(16);
}
async1();
// Simulate a delay
let startTime = Date.now();
console.log("Starting 5-second delay");
while (Date.now() - startTime < 5000) {}
console.log("Delay completed");
Function Scope and Undefined Operations
var message = "Hello";
var number = 1;
var undefinedVar;
(function () {
var localVar = "World";
console.log(message + localVar);
})();
console.log(message + undefinedVar);
console.log(number + undefinedVar);
console.log(message + localVar);
// Output: HelloWorld
// Helloundefined (string concatenation)
// NaN (number + undefined)
// ReferenceError: localVar is not defined (function scope)
Closure in Event Handlers
// Problem: When clicking buttons, all alert the same value
var addHandlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i++) {
nodes[i].onclick = function (e) {
alert(i);
};
}
};
var buttons = document.getElementsByTagName("button");
addHandlers(buttons);
// Solution: Use closure to preserve loop variable value
for (var i = 0; i < nodes.length; i++) {
(function (index) {
nodes[index].onclick = function (e) {
alert(index);
};
})(i);
}
Nested Function Scope
var outerFunction = function () {
var a = 3,
b = 5;
var innerFunction = function () {
var b = 7,
c = 11;
console.log(`One: a:${a} b:${b} c:${c}`);
a += b + c;
console.log(`Two: a:${a} b:${b} c:${c}`);
};
console.log(`Three: a:${a} b:${b} c:${c}`);
innerFunction();
console.log(`Four: a:${a} b:${b} c:${c}`);
};
outerFunction();
// Output:
// Three: a:3 b:5 c:undefined
// One: a:3 b:7 c:11
// Two: a:21 b:7 c:11
// Four: a:21 b:5 c:undefined
Variable Scope and 'this' Context
var globalName = "Global Name";
var personObject = (function () {
var localName = "Local Name"; // Function-scoped variable
return {
name: "Object Name",
sayName: function () {
var innerName = "Inner Name"; // Function-scoped variable
console.log(this.name);
}
}
})();
console.log(globalName); // Global Name
console.log(personObject.name); // Object Name
personObject.sayName(); // Object Name (this points to personObject)
personObject.sayName.call(window); // Global Name (this explicitly set to window)
window.addEventListener("DOMContentLoaded", personObject.sayName); // undefined (this points to event target - window)
Variable Hoisting and Event Loop
console.log(variableA); // [Function: variableA] - function declaration is hoisted
console.log(variableA()); // undefined - function has no return value
var variableA = 1; // Reassign variableA to a number
function variableA() {
setTimeout(function () {
console.log(2); // Microtask
}, 0);
Promise.resolve().then(function () {
console.log(3); // Macrotask
});
console.log(4);
}
console.log(variableA); // 1
variableA = 5;
console.log(variableA); // 5
// Output: [Function: variableA] 4 undefined 1 5 3 2
Shallow and Deep Copying
const originalObject = {
value: 1,
nested: {
value: 2,
},
};
const shallowCopy = Object.assign({}, originalObject); // Shallow copy
const deepCopy = JSON.parse(JSON.stringify(originalObject)); // Deep copy
originalObject.value = 10;
originalObject.nested.value = 20;
console.log(originalObject); // {value:10,nested:{value:20}}
console.log(shallowCopy); // {value:1,nested:{value:20}}
console.log(deepCopy); // {value:1,nested:{value:2}}
Color Sorting
let items = [
{ color: "black", size: 10 },
{ color: "black", size: 5 },
{ color: "white", size: 5 },
{ color: "white", size: 10 },
{ color: "red", size: 12 },
{ color: "red", size: 9 },
{ color: "blue", size: 9 },
{ color: "green", size: 6 },
{ color: "orange", size: 55 },
{ color: "orange", size: 5 },
{ color: "orange", size: 5 },
];
const colorPriority = ["black", "white", "red", "blue", "green", "orange"];
items.sort((a, b) => {
const colorIndexA = colorPriority.indexOf(a.color);
const colorIndexB = colorPriority.indexOf(b.color);
if (colorIndexA !== colorIndexB) {
return colorIndexA - colorIndexB;
} else {
return a.size - b.size;
}
});
console.log(items);
Algorithm Implementation
Array to Tree Conversion
function flattenArrayToTree(items, parentId = null) {
const tree = [];
for (const item of items) {
if (item.parentId === parentId) {
const children = flattenArrayToTree(items, item.id);
if (children.length > 0) {
item.children = children;
}
tree.push(item);
}
}
return tree;
}
// Example data
const flatData = [
{ id: 1, name: "Parent 1", parentId: null },
{ id: 2, name: "Parent 2", parentId: null },
{ id: 3, name: "Child 1.1", parentId: 1 },
{ id: 4, name: "Child 1.2", parentId: 1 },
{ id: 5, name: "Child 2.1", parentId: 2 },
{ id: 6, name: "Grandchild 1.1.1", parentId: 3 },
];
const hierarchicalData = flattenArrayToTree(flatData);
console.log(JSON.stringify(hierarchicalData, null, 2));
String Reversal
/**
* Reverses a string without using built-in reverse function
* @param {string} inputString
* @return {string} Reversed string
* @example
* reverseString("Hello, World!") => "!dlroW ,olleH"
*/
function reverseString(inputString) {
let reversed = '';
for (let i = inputString.length - 1; i >= 0; i--) {
reversed += inputString.charAt(i);
}
return reversed;
}
// Test
const testString = "JavaScript is awesome!";
const reversedString = reverseString(testString);
console.log(reversedString); // Output: "!emosewa si tpircSavaJ"
Greedy Algorithm: Interval Merging
/**
* Merges overlapping intervals
* @param {Array} intervals Array of intervals, each interval is [start, end]
* @return {Array} Merged intervals
* @example
* mergeIntervals([[1,3],[2,6],[8,10],[15,18]]) => [[1,6],[8,10],[15,18]]
*/
function mergeIntervals(intervals) {
if (intervals.length <= 1) {
return intervals;
}
// Sort intervals by start time
intervals.sort((a, b) => a[0] - b[0]);
const merged = [intervals[0]];
for (let i = 1; i < intervals.length; i++) {
const currentInterval = intervals[i];
const lastMergedInterval = merged[merged.length - 1];
// If current interval overlaps with the last merged interval
if (currentInterval[0] <= lastMergedInterval[1]) {
// Merge them by taking the maximum end time
lastMergedInterval[1] = Math.max(lastMergedInterval[1], currentInterval[1]);
} else {
// Otherwise, add the current interval to the result
merged.push(currentInterval);
}
}
return merged;
}
const testIntervals = [[1, 3], [2, 6], [8, 10], [15, 18]];
const mergedIntervals = mergeIntervals(testIntervals);
console.log(mergedIntervals); // Output: [[1,6],[8,10],[15,18]]