Various Code Examples And Patterns
call, bind, and apply
JavaScript’s call(), bind(), and apply() methods are powerful tools for controlling function context (this) at runtime.
They are especially useful when borrowing methods, delaying execution, or separating data from behavior.
- call()immediately invokes the function with a specified- thiscontext and arguments.
- apply()is similar to- call(), but it takes arguments as an array.
- bind()returns a new function with a permanently bound context.
These methods are commonly used when:
- Reusing methods across different objects
- Passing methods as callbacks without losing context
- Implementing decorators or delayed invocation
- Writing functional utilities like rateLimit
This section also demonstrates how arrow functions behave differently with this, and includes a custom implementation of Function.prototype.bind.
Example: Losing and Rebinding Context
This example demonstrates what happens when a method loses its context (this) and how to recover it using call() and bind():
- Calling counter.add(1)works as expected —thisrefers tocounter.
- When passing counter.adddirectly to another function (operate), it loses context and throws an error.
- Using call()explicitly sets the context.
- Using bind()returns a new function withthispermanently bound tocounter.
const counter = {
  value: 10,
  add(num) {
    this.value += num;
    return this.value;
  },
};
 
function operate(method, num) {
  return method(num);
}
 
function operateWithCall(method, num) {
  return method.call(counter, num);
}
 
console.log(counter.add(1)); // 11
console.log(operateWithCall(counter.add, 1)); // 12
console.log(operate(counter.add.bind(counter), 1)); // 13
try {
  console.log(operate(counter.add, 1));
} catch (e) {
  console.error(e.message);
  // Cannot read properties of undefined (reading 'value')
}Example 1: Using call and apply to Change Context
This example shows how call() and apply() can be used to invoke a method with a different this context:
- objA.print.call(objB)executes- objA.printwith- objBas- this, so it logs- "I am from objB".
- apply()works similarly, but takes arguments as an array.
const objA = {
  data: "I am from objA",
  print() {
    console.log(this.data);
  },
};
 
const objB = {
  data: "I am from objB",
};
 
objA.print.call(objB); // I am from objB
objA.print.apply(objB); // I am from objBExample 2: Preserving Method Context with setTimeout
This example shows how to preserve the this context when calling a method asynchronously using setTimeout.
The delayMethod function returns a wrapper that defers method execution by 1 second while ensuring that this still refers to the original object.
- user.greet()uses- this.name, so it’s crucial to call it with the correct context.
- Wrapping it with obj[methodName](...args)inside the timeout maintains the correctthis.
function delayMethod(obj, methodName) {
  return function (...args) {
    setTimeout(() => obj[methodName](...args), 1000);
  };
}
 
const user = {
  name: "Alice",
  greet(greeting) {
    console.log(`${greeting}, ${this.name}!`);
  },
};
 
const delayedGreet = delayMethod(user, "greet");
delayedGreet("Hello"); // After 1 second: "Hello, Alice!"Example 3: Custom bind() Implementation
This example demonstrates how to implement a simplified version of Function.prototype.bind().
The customBind function:
- Stores a reference to the original function.
- Returns a new function that, when called, invokes the original with a fixed thiscontext and any partially applied arguments.
This mimics native bind() behavior, including partial application of arguments.
Function.prototype.customBind = function (context, ...args) {
  const originalFunction = this;
  return function (...newArgs) {
    return originalFunction.apply(context, [...args, ...newArgs]);
  };
};
 
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}
 
const person = { name: "John" };
const boundGreet = greet.customBind(person, "Hello");
boundGreet("!"); // "Hello, John!"Example 4: Sharing Methods Across Objects with call()
In this example, the calculateTotal method from store1 is reused for store2 by using Function.prototype.call().
- call()sets- thisto- store2, so the method computes tax using- store2.taxRate.
- As an alternative, the method is directly assigned to store2, allowing normal invocation syntax.
const store1 = {
  taxRate: 0.1,
  calculateTotal(price, quantity) {
    const subtotal = price * quantity;
    return subtotal + subtotal * this.taxRate;
  },
};
 
const store2 = {
  taxRate: 0.2,
};
 
const price = 50;
const quantity = 2;
 
// Calculate total for `store2` using the method from `store1`
const total = store1.calculateTotal.call(store2, price, quantity);
console.log(total); // 120 (price * quantity + tax)
 
// 2 solution
store2.calculateTotal = store1.calculateTotal;
console.log(store2.calculateTotal(price, quantity));Example 5: bind() for Context in Rate-Limited Function
This example demonstrates how to use bind() to ensure the correct this context when passing an object's method to a wrapper function.
- logger.logis passed into- rateLimit()which returns a throttled version of the method.
- Since logrelies onthis.message,bind(logger)is required to preserve the context.
- Without bind(),this.messagewould beundefinedwhenlog()is called inside the throttled wrapper.
function rateLimit(fn, ms) {
  let lastCall = 0;
 
  return function (...args) {
    const now = Date.now();
 
    if (now - lastCall < ms) return;
    lastCall = now;
    return fn(...args);
  };
}
 
const logger = {
  message: "Rate limited log",
  log() {
    console.log(this.message);
  },
};
 
const rateLimitedLog = rateLimit(logger.log.bind(logger), 1000);
rateLimitedLog(); // Rate limited log
rateLimitedLog();
setTimeout(rateLimitedLog, 1500);
// Logs "Rate limited log" after 1.5sExample 6: Using bind() to Preserve this
In this example, greet() is a regular function that relies on this.name. When it's not called in the context of an object, this would be undefined.
To ensure the correct context (person), we create a bound version of the function using bind(person).
This guarantees that this.name inside greet() always refers to "John", even if called independently.
const person = {
  name: "John",
};
 
function greet() {
  console.log(`Hello, ${this.name}`);
}
 
// Create a bound function
const boundGreet = greet.bind(person);
boundGreet(); // Hello, JohnExample 7: Arrow Functions Ignore call
In this example, the arrow function is defined inside the greet() method using an arrow function.
Arrow functions do not have their own this — they inherit this from their enclosing lexical context.
So even when using call() with a different this value ({ name: "Bob" }), the arrow function still uses
this.name from the outer greet() context, which is user.
Thus, the output is: Hello, Alice.
const user = {
  name: "Alice",
  greet() {
    const arrow = () => console.log(`Hello, ${this.name}`);
    arrow.call({ name: "Bob" }); // Arrow functions ignore `call`
  },
};
 
user.greet(); // Hello, AliceExample 8: Preserving this in Callbacks
When passing a method like user.logName to another function (e.g. executeCallback), the this context is lost unless it is explicitly preserved.
- bind(user)ensures that- thisalways refers to- user.
- An arrow function () => user.logName()also preserves the context by calling the method directly onuser.
Both approaches ensure the correct this context, so the output is: Alice.
const user = {
  name: "Alice",
  logName() {
    console.log(this.name);
  },
};
 
function executeCallback(callback) {
  callback();
}
 
// Preserve context with `bind`
executeCallback(user.logName.bind(user)); // Alice
executeCallback(() => user.logName()); // Aliceclsx
The clsx utility is commonly used to conditionally join class names in JavaScript and React projects.
This custom implementation mimics the behavior of the clsx library by:
- Skipping falsy values (false,null,undefined,0,"",NaN)
- Joining strings as-is
- Recursively flattening arrays
- Including keys from objects whose values are truthy
This allows for clean, readable class name composition in UI code.
function clsx(...args) {
  const classes = [];
 
  for (const arg of args) {
    // Skip the current iteration if the argument is falsy
    if (!arg) continue;
 
    if (typeof arg === "string") {
      classes.push(arg);
    } else if (Array.isArray(arg)) {
      classes.push(clsx(...arg)); // Recursively process arrays
    } else if (typeof arg === "object") {
      for (const key in arg) {
        if (arg[key]) {
          classes.push(key); // Push key if value is truthy
        }
      }
    }
  }
 
  return classes.join(" "); // Join classes with a space
}
 
console.log(
  clsx("base-class", { active: true, disabled: false }, [
    "additional-class",
    "another-class",
  ]),
); // base-class active additional-class another-class
console.log(
  clsx(null, false, "bar", undefined, { baz: null }, "", [[[{ one: 1 }]]]),
); // bar onecontains duplicate
This function checks if a given array contains any duplicate values.
It uses a Set to track elements that have already been seen while iterating through the array.
If a value is found in the Set, the function returns true, indicating a duplicate.
If no duplicates are found by the end of iteration, it returns false.
function containsDuplicate(nums) {
  const seen = new Set();
 
  for (const num of nums) {
    if (seen.has(num)) {
      return true;
    }
 
    seen.add(num);
  }
 
  return false;
}
 
console.log(containsDuplicate([1, 1, 3, 4])); // true
console.log(containsDuplicate([1, 3, 4])); // falsedeep clone
The deepClone function recursively copies all levels of an object or array, creating a fully independent clone.
This avoids shared references and ensures that changes to nested structures in the cloned object won’t affect the original.
It handles:
- Primitive types and null
- Arrays using recursive map
- Plain objects using recursive property traversal
Perfect for deep copying JSON-compatible data without using JSON.parse(JSON.stringify(...)),
which fails on special types like Date, Map, Set, or circular references.
function deepClone(obj) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
 
  if (Array.isArray(obj)) {
    // Recursively clone array elements
    return obj.map(deepClone);
  }
 
  const clonedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      // Recursively clone object properties
      clonedObj[key] = deepClone(obj[key]);
    }
  }
 
  return clonedObj;
}
 
const obj = { a: 1, b: [{ c: 2, d: 3 }] };
const clonedObj = deepClone(obj);
console.log(clonedObj); // { a: 1, b: [ { c: 2, d: 3 } ] }dictionary of nested arrays
This example demonstrates how to transform a deeply nested array structure (e.g., categories → subcategories → items)
into a dictionary (object-based) format for fast lookup by id.
Benefits:
- Constant-time access to categories, subcategories, and items by their id.
- Improves performance for large datasets with frequent lookups.
- Easy to traverse and mutate without relying on nested find()calls.
Approaches Covered:
- Plain JS implementation using nested loops to assign items into objects keyed by id.
- Recursive mapToDictionaryutility that supports arbitrary nesting levels via akeysargument.
- TypeScript version with strict typing and reusable helper functions for building deeply nested dictionaries.
Great for building normalized state for UIs, lookup tables, or improving nested data traversal.
const data = [
  {
    id: 1,
    name: "Category A",
    items: [
      {
        id: 2,
        name: "Subcategory A1",
        items: [
          { id: 3, name: "Item A1-1", value: 10 },
          { id: 4, name: "Item A1-2", value: 15 },
        ],
      },
      {
        id: 5,
        name: "Subcategory A2",
        items: [
          { id: 6, name: "Item A2-1", value: 20 },
          { id: 7, name: "Item A2-2", value: 25 },
        ],
      },
    ],
  },
  {
    id: 8,
    name: "Category B",
    items: [
      {
        id: 9,
        name: "Subcategory B1",
        items: [
          { id: 10, name: "Item B1-1", value: 30 },
          { id: 11, name: "Item B1-2", value: 35 },
        ],
      },
      {
        id: 12,
        name: "Subcategory B2",
        items: [
          { id: 13, name: "Item B2-1", value: 40 },
          { id: 14, name: "Item B2-2", value: 45 },
        ],
      },
    ],
  },
];
 
function createNestedDictionary(data) {
  const dictionary = {};
 
  for (const category of data) {
    dictionary[category.id] = { ...category, subcategories: {} };
 
    for (const subcategory of category.items) {
      dictionary[category.id].subcategories[subcategory.id] = {
        ...subcategory,
        items: {},
      };
 
      for (const item of subcategory.items) {
        dictionary[category.id].subcategories[subcategory.id].items[item.id] =
          item;
      }
    }
  }
 
  return dictionary;
}
 
const nestedDictionary = createNestedDictionary(data);
 
console.log(nestedDictionary[1].name); // Category A
console.log(nestedDictionary[1].subcategories[5].name); // Subcategory A2
console.log(nestedDictionary[1].subcategories[5].items[7].name); // Item A2-2
console.log(nestedDictionary[8].subcategories[12].items[14]);
// { id: 14, name: 'Item B2-2', value: 45 }const data = [
  {
    id: 1,
    name: "Category A",
    items: [
      {
        id: 2,
        name: "Subcategory A1",
        items: [
          { id: 3, name: "Item A1-1", value: 10 },
          { id: 4, name: "Item A1-2", value: 15 },
        ],
      },
      {
        id: 5,
        name: "Subcategory A2",
        items: [
          { id: 6, name: "Item A2-1", value: 20 },
          { id: 7, name: "Item A2-2", value: 25 },
        ],
      },
    ],
  },
  {
    id: 8,
    name: "Category B",
    items: [
      {
        id: 9,
        name: "Subcategory B1",
        items: [
          { id: 10, name: "Item B1-1", value: 30 },
          { id: 11, name: "Item B1-2", value: 35 },
        ],
      },
      {
        id: 12,
        name: "Subcategory B2",
        items: [
          { id: 13, name: "Item B2-1", value: 40 },
          { id: 14, name: "Item B2-2", value: 45 },
        ],
      },
    ],
  },
];
 
function mapToDictionary(data, keys) {
  const [currentKey, ...remainingKeys] = keys;
 
  return data.reduce((acc, item) => {
    acc[item.id] = {
      ...item,
      [currentKey || "items"]: item.items
        ? mapToDictionary(item.items, remainingKeys)
        : undefined,
    };
    return acc;
  }, {});
}
 
const nestedDictionary = mapToDictionary(data, ["subcategories", "items"]);
 
console.log(nestedDictionary[1].name); // Category A
console.log(nestedDictionary[1].subcategories[5].name); // Subcategory A2
console.log(nestedDictionary[1].subcategories[5].items[7].name); // Item A2-2
console.log(nestedDictionary[8].subcategories[9].items[11]);
// { id: 11, name: 'Item B1-2', value: 35, items: undefined }typescript
type Item = {
  id: number;
  name: string;
  value: number;
};
 
type Subcategory = {
  id: number;
  name: string;
  items: Item[];
};
 
type Category = {
  id: number;
  name: string;
  items: Subcategory[];
};
 
type Subcategories = Record<
  number,
  {
    name: string;
    items: Record<number, Item>;
  }
>;
 
type NestedDictionary = Record<
  number,
  {
    name: string;
    subcategories: Subcategories;
  }
>;
 
function mapItemsToDictionary(items: Item[]) {
  return items.reduce(
    (acc, item) => {
      acc[item.id] = item;
      return acc;
    },
    {} as Record<number, Item>,
  );
}
 
function mapSubcategoriesToDictionary(
  subcategories: Subcategory[],
) {
  return subcategories.reduce((acc, subcategory) => {
    acc[subcategory.id] = {
      name: subcategory.name,
      items: mapItemsToDictionary(subcategory.items),
    };
    return acc;
  }, {} as Subcategories);
}
 
function createNestedDictionary(
  categories: Category[],
): NestedDictionary {
  return categories.reduce((acc, category) => {
    acc[category.id] = {
      name: category.name,
      subcategories: mapSubcategoriesToDictionary(
        category.items,
      ),
    };
    return acc;
  }, {} as NestedDictionary);
}
 
const data: Category[] = [
  {
    id: 1,
    name: "Category A",
    items: [
      {
        id: 2,
        name: "Subcategory A1",
        items: [
          { id: 3, name: "Item A1-1", value: 10 },
          { id: 4, name: "Item A1-2", value: 15 },
        ],
      },
      {
        id: 5,
        name: "Subcategory A2",
        items: [
          { id: 6, name: "Item A2-1", value: 20 },
          { id: 7, name: "Item A2-2", value: 25 },
        ],
      },
    ],
  },
  {
    id: 8,
    name: "Category B",
    items: [
      {
        id: 9,
        name: "Subcategory B1",
        items: [
          { id: 10, name: "Item B1-1", value: 30 },
          { id: 11, name: "Item B1-2", value: 35 },
        ],
      },
      {
        id: 12,
        name: "Subcategory B2",
        items: [
          { id: 13, name: "Item B2-1", value: 40 },
          { id: 14, name: "Item B2-2", value: 45 },
        ],
      },
    ],
  },
];
 
const nestedDictionary = createNestedDictionary(data);
 
console.log(nestedDictionary[1].name); // Category A
console.log(nestedDictionary[1].subcategories[5].name); // Subcategory A2
console.log(
  nestedDictionary[1].subcategories[5].items[7].name,
); // Item A2-2
console.log(
  nestedDictionary[8].subcategories[12].items[14].value,
); // 45filter by related property
This utility filters a list of objects based on a related property found in another array — similar to a join + filter operation.
In this example, we filter items in the objects array based on the class value of their related object_type, which is matched against the object_types array.
Highlights:
- groupBy()helps group- object_typesby- classfor fast lookup.
- filterObjectsByClass()efficiently filters items by matching related object type's class.
- Uses TypeScript generics for reusability and type safety.
const objects = [
  { id: 1, name: "Test 1", object_type: 1 },
  { id: 2, name: "Test 2", object_type: 1 },
  { id: 3, name: "Test 3", object_type: 2 },
  { id: 4, name: "Test 4", object_type: 3 },
];
 
const object_types = [
  { id: 1, class: "orange" },
  { id: 2, class: "orange" },
  { id: 3, class: "apple" },
  { id: 4, class: "cheese" },
];
 
const groupBy = <T, K extends string | number | symbol>(
  arr: T[],
  callback: (item: T) => K,
): Record<K, T[]> => {
  return arr.reduce(
    (acc: Record<K, T[]>, item: T) => {
      const key = callback(item);
      if (!acc[key]) acc[key] = [];
      acc[key].push(item);
 
      return acc;
    },
    {} as Record<K, T[]>,
  );
};
 
const filterObjectsByClass = <T>(
  cls: string,
  objects: (T & { object_type: number })[],
  objectTypes: { id: number; class: string }[],
): T[] => {
  const result: T[] = [];
 
  const objTypesByClass = groupBy(
    objectTypes,
    (item) => item.class,
  );
 
  for (const item of objects) {
    if (
      objTypesByClass[cls].find(
        (objectType) => objectType.id === item.object_type,
      )
    ) {
      result.push(item);
    }
  }
 
  return result;
};
 
const filteredObjects = filterObjectsByClass(
  "orange",
  objects,
  object_types,
);
console.log(filteredObjects);
 
// [
//   { id: 1, name: 'Test 1', object_type: 1 },
//   { id: 2, name: 'Test 2', object_type: 1 },
//   { id: 3, name: 'Test 3', object_type: 2 }
// ]filterMap
The filterMap() utility combines filtering and mapping into a single reduce() pass — making it more efficient than chaining .filter().map().
This function takes:
- an array,
- a filterBooleanfunction to determine which elements to include,
- and a mapCallbackto transform each included item.
export const filterMap = (array, filterBoolean, mapCallback) => {
  return array.reduce((acc, item, idx) => {
    if (filterBoolean(item)) {
      acc.push(mapCallback(item, idx));
    }
    return acc;
  }, []);
};
 
const people = [
  { name: "Alice", age: 25, active: true },
  { name: "Bob", age: 30, active: false },
  { name: "Charlie", age: 35, active: true },
];
 
const activeNames = filterMap(
  people,
  (person) => person.active,
  (person) => person.name,
);
 
console.log(activeNames); // ['Alice', 'Charlie']flat nested
Transforms deeply nested category-subcategory-item data into a flat array where each item contains metadata about its parent subcategory and category.
You can do it manually with loops or concisely with flatMap().
const data = [
  {
    id: 1,
    name: "Category A",
    items: [
      {
        id: 2,
        name: "Subcategory A1",
        items: [
          { id: 3, name: "Item A1-1", value: 10 },
          { id: 4, name: "Item A1-2", value: 15 },
        ],
      },
      {
        id: 5,
        name: "Subcategory A2",
        items: [
          { id: 6, name: "Item A2-1", value: 20 },
          { id: 7, name: "Item A2-2", value: 25 },
        ],
      },
    ],
  },
  {
    id: 8,
    name: "Category B",
    items: [
      {
        id: 9,
        name: "Subcategory B1",
        items: [
          { id: 10, name: "Item B1-1", value: 30 },
          { id: 11, name: "Item B1-2", value: 35 },
        ],
      },
      {
        id: 12,
        name: "Subcategory B2",
        items: [
          { id: 13, name: "Item B2-1", value: 40 },
          { id: 14, name: "Item B2-2", value: 45 },
        ],
      },
    ],
  },
];
 
function transformItems(data) {
  const transformedItems = [];
 
  for (const category of data) {
    for (const subcategory of category.items) {
      for (const item of subcategory.items) {
        transformedItems.push({
          id: item.id,
          name: item.name,
          value: item.value,
          subcategory: subcategory.name,
          category: category.name,
        });
      }
    }
  }
 
  return transformedItems;
}
 
const itemsWithCategories = transformItems(data);
 
console.log(itemsWithCategories);
 
// [
//   {
//     id: 3,
//     name: "Item A1-1",
//     value: 10,
//     subcategory: "Subcategory A1",
//     category: "Category A",
//   },
//   {
//     id: 4,
//     name: "Item A1-2",
//     value: 15,
//     subcategory: "Subcategory A1",
//     category: "Category A",
//   },
//   {
//     id: 6,
//     name: "Item A2-1",
//     value: 20,
//     subcategory: "Subcategory A2",
//     category: "Category A",
//   },
//   {
//     id: 7,
//     name: "Item A2-2",
//     value: 25,
//     subcategory: "Subcategory A2",
//     category: "Category A",
//   },
//   {
//     id: 10,
//     name: "Item B1-1",
//     value: 30,
//     subcategory: "Subcategory B1",
//     category: "Category B",
//   },
//   {
//     id: 11,
//     name: "Item B1-2",
//     value: 35,
//     subcategory: "Subcategory B1",
//     category: "Category B",
//   },
//   {
//     id: 13,
//     name: "Item B2-1",
//     value: 40,
//     subcategory: "Subcategory B2",
//     category: "Category B",
//   },
//   {
//     id: 14,
//     name: "Item B2-2",
//     value: 45,
//     subcategory: "Subcategory B2",
//     category: "Category B",
//   },
// ];
 
function transformItems2(data) {
  return data.flatMap((category) =>
    category.items.flatMap((subcategory) =>
      subcategory.items.map((item) => ({
        id: item.id,
        name: item.name,
        value: item.value,
        subcategory: subcategory.name,
        category: category.name,
      })),
    ),
  );
}
 
const itemsWithCategories2 = transformItems2(data);
console.log(itemsWithCategories2);
 
// ...same resultgroup list by quarters
Groups monthly financial data by calendar quarters using the getQuarter() method from
the date-fns library. Each group includes individual monthly entries and an optional cumulative total for the quarter.
Useful for:
- Visualizing quarterly reports
- Aggregating financial or performance data
- Preparing charting datasets or grouped UI views
Steps:
- groupDataByQuarter(data)– Organizes data into a dictionary using keys like- "2023-Q1"based on the- datefield.
- initializeDates(groupedQuarters)– Prepares a simplified structure listing quarters and associated month strings.
- initializeRestData(groupedQuarters)– Flattens the quarterly structure into tax-specific groups, returning an array of metrics (- profit,- profit_percent) with their per-quarter values and totals.
This modular approach is helpful when formatting data for analytics dashboards or quarterly reports.
import { getQuarter, parseISO } from "date-fns";
 
// data
export const TaxEntitiesKeys = {
  profit: "profit",
  profit_percent: "profit_percent",
};
 
const mockTaxes = {
  cumulative: [
    { date: "2023-01-31", profit: 1000, profit_percent: 100 },
    { date: "2023-02-28", profit: 2000, profit_percent: 200 },
    { date: "2023-04-30", profit: 3000, profit_percent: 250 },
  ],
  report: [
    { date: "2023-01-31", profit: 300, profit_percent: 30 },
    { date: "2023-02-28", profit: 500, profit_percent: 50 },
    { date: "2023-04-30", profit: 700, profit_percent: 70 },
  ],
};
// data
 
// group by quarters
const groupDataByQuarter = (data) => {
  const quarters = {};
 
  // Group data into quarters based on their date
  for (const monthData of data.report) {
    const date = parseISO(monthData.date);
    const year = date.getFullYear();
    const quarter = getQuarter(date);
    const key = `${year}-Q${quarter}`;
 
    if (!quarters[key]) {
      quarters[key] = { months: [], total: null };
    }
    quarters[key].months.push(monthData);
  }
 
  // Assign cumulative totals to their respective quarters
  for (const totalData of data.cumulative) {
    const date = parseISO(totalData.date);
    const year = date.getFullYear();
    const quarter = getQuarter(date);
    const key = `${year}-Q${quarter}`;
 
    if (quarters[key]) {
      quarters[key].total = totalData;
    }
  }
 
  return quarters;
};
 
const groupedQuarters = groupDataByQuarter(mockTaxes);
console.log(JSON.stringify({ groupedQuarters }, null, 2));
// group by quarters
 
// {
//   groupedQuarters: {
//     "2023-Q1": {
//       months: [
//         {
//           date: "2023-01-31",
//           profit: 300,
//           profit_percent: 30,
//         },
//         {
//           date: "2023-02-28",
//           profit: 500,
//           profit_percent: 50,
//         },
//       ],
//       total: {
//         date: "2023-02-28",
//         profit: 2000,
//         profit_percent: 200,
//       },
//     },
//     "2023-Q2": {
//       months: [
//         {
//           date: "2023-04-30",
//           profit: 700,
//           profit_percent: 70,
//         },
//       ],
//       total: {
//         date: "2023-04-30",
//         profit: 3000,
//         profit_percent: 250,
//       },
//     },
//   },
// };
 
// dates
const initializeDates = (quarters) => {
  const dates = [];
 
  Object.entries(quarters).forEach(([dateKey, data]) => {
    const [year, quarter] = dateKey.split("-Q").map(Number);
 
    let yearEntry = dates.find((entry) => entry.year === year);
    if (!yearEntry) {
      yearEntry = { year, periods: [] };
      dates.push(yearEntry);
    }
 
    yearEntry.periods.push({
      months: data.months.map((month) => month.date),
      quarter: `${quarter}`,
    });
  });
 
  return dates;
};
 
console.log(
  JSON.stringify({ dates: initializeDates(groupedQuarters) }, null, 2),
);
// dates
 
// {
//   dates: [
//     {
//       year: 2023,
//       periods: [
//         {
//           months: ["2023-01-31", "2023-02-28"],
//           quarter: "1",
//         },
//         {
//           months: ["2023-04-30"],
//           quarter: "2",
//         },
//       ],
//     },
//   ],
// };
 
// rest data
const initializeRestData = (quarters) => {
  return Object.keys(TaxEntitiesKeys).map((taxKey) => {
    const taxKeyTyped = taxKey;
 
    return {
      title: taxKeyTyped,
      periods: Object.entries(quarters).map(([_, data]) => ({
        months: data.months.map((month) => month[taxKeyTyped]),
        total: data.total ? data.total[taxKeyTyped] : null,
      })),
    };
  });
};
 
console.log(
  JSON.stringify({ restData: initializeRestData(groupedQuarters) }, null, 2),
);
// rest data
 
// {
//   restData: [
//     {
//       title: "profit",
//       periods: [
//         {
//           months: [300, 500],
//           total: 2000,
//         },
//         {
//           months: [700],
//           total: 3000,
//         },
//       ],
//     },
//     {
//       title: "profit_percent",
//       periods: [
//         {
//           months: [30, 50],
//           total: 200,
//         },
//         {
//           months: [70],
//           total: 250,
//         },
//       ],
//     },
//   ],
// };innerJoin
Implements a basic innerJoin function for
arrays — similar to SQL inner joins. This method takes a predicate, a list of records, and a list of ids.
It returns all records where the predicate returns true for at least one ID.
This approach is helpful for:
- Matching entities across two datasets
- Resolving references between relational data
- Filtering records based on foreign key relations
The example joins musician records by matching their id with an array of selected IDs.
function innerJoin(predicate, records, ids) {
  return records.filter((record) => ids.some((id) => predicate(record, id)));
}
 
const result = innerJoin(
  (record, id) => record.id === id,
  [
    { id: 824, name: "Richie Furay" },
    { id: 956, name: "Dewey Martin" },
    { id: 313, name: "Bruce Palmer" },
    { id: 456, name: "Stephen Stills" },
    { id: 177, name: "Neil Young" },
  ],
  [177, 456, 999],
);
 
console.log(result);
// [{id: 456, name: 'Stephen Stills'}, {id: 177, name: 'Neil Young'}]list to tree
This utility transforms a flat list of items (with id and parentId fields) into a nested tree structure.
It uses a Map to efficiently link children to their parents while preserving the hierarchy.
It also includes a treeToList() function that flattens a tree back into a list — useful for data normalization,
storing tree nodes in databases, or syncing front-end trees with backend updates.
function listToTree(items) {
  const map = new Map();
  const roots = [];
 
  // Map items by ID
  for (const item of items) {
    map.set(item.id, { ...item, children: [] });
  }
 
  for (const item of items) {
    const node = map.get(item.id);
 
    if (item.parentId === null) {
      roots.push(node);
    } else {
      const parent = map.get(item.parentId);
 
      if (parent) {
        parent.children.push(node);
      }
    }
  }
 
  return roots;
}
 
const items = [
  { id: 1, name: "Root 1", parentId: null },
  { id: 2, name: "Child 1.1", parentId: 1 },
  { id: 3, name: "Child 1.2", parentId: 1 },
  { id: 4, name: "Root 2", parentId: null },
  { id: 5, name: "Child 2.1", parentId: 4 },
  { id: 6, name: "SubChild 2.1.1", parentId: 5 },
];
 
const tree = listToTree(items);
console.log(tree);
// [
//   {
//     "id": 1,
//     "name": "Root 1",
//     "parentId": null,
//     "children": [
//       {
//         "id": 2,
//         "name": "Child 1.1",
//         "parentId": 1,
//         "children": []
//       },
//       {
//         "id": 3,
//         "name": "Child 1.2",
//         "parentId": 1,
//         "children": []
//       }
//     ]
//   },
//   {
//     "id": 4,
//     "name": "Root 2",
//     "parentId": null,
//     "children": [
//       {
//         "id": 5,
//         "name": "Child 2.1",
//         "parentId": 4,
//         "children": [
//           {
//             "id": 6,
//             "name": "SubChild 2.1.1",
//             "parentId": 5,
//             "children": []
//           }
//         ]
//       }
//     ]
//   }
// ]
 
function treeToList(tree) {
  const list = [];
 
  function traverse(node) {
    const { children, ...rest } = node;
    list.push(rest);
 
    if (children) {
      for (const child of children) {
        traverse(child);
      }
    }
  }
 
  for (const root of tree) {
    traverse(root);
  }
 
  return list;
}
 
const flatList = treeToList(tree);
console.log(flatList);
// [
//   { id: 1, name: 'Root 1', parentId: null },
//   { id: 2, name: 'Child 1.1', parentId: 1 },
//   { id: 3, name: 'Child 1.2', parentId: 1 },
//   { id: 4, name: 'Root 2', parentId: null },
//   { id: 5, name: 'Child 2.1', parentId: 4 },
//   { id: 6, name: 'SubChild 2.1.1', parentId: 5 }
// ]promises, closures and event loop mix
This section demonstrates how JavaScript promises, closures, and the event loop interact in real-world scenarios. These examples cover:
- How closures preserve state across async calls
- What happens when you awaitvs. run multiple async calls in parallel
- The importance of capturing variables in closures before setTimeout
- How the event loop schedules tasks with setTimeout,Promise, andconsole.log
- Sequential chaining of promises with closures
Perfect for understanding async behavior, state retention, and timing execution in complex JavaScript code.
Example 1: Async function with closure and delayed execution
This example shows how closures capture state (count) and how await affects the execution order.
Each call to asyncCounter() increments count and logs it immediately. However, await delays further code inside the async function,
allowing subsequent calls to update count before the first finishes.
- Multiple calls to the same async function share the same closure state.
- console.log("Script complete")runs immediately because- awaityields control to the event loop.
function createAsyncCounter() {
  let count = 0;
  return async function incrementAsyncCounter() {
    count++;
    console.log({ count });
    await new Promise((resolve) => setTimeout(resolve, 1000));
    console.log("Async Counter:", count);
  };
}
 
const asyncCounter = createAsyncCounter();
 
asyncCounter();
asyncCounter().then(() => {
  asyncCounter();
});
 
console.log("Script complete");
 
// { count: 1 }
// { count: 2 }
// Script complete
// 1 s delay
// Async Counter: 2
// Async Counter: 2
// { count: 3 }
// 1 s delay
// Async Counter: 3Example 2: Sequential async calls with await and closure
This example demonstrates how sequential await calls work with closures. Each async call completes before the next one begins,
ensuring the counter increments correctly. The count variable is preserved across invocations due to the closure, and each setTimeout
resolves after 1 second, leading to predictable, step-by-step output.
function createAsyncCounter() {
  let count = 0;
  return async function incrementAsyncCounter() {
    count++;
    await new Promise((resolve) => setTimeout(resolve, 1000));
    console.log("Async Counter:", count);
  };
}
 
const asyncCounter = createAsyncCounter();
 
await asyncCounter();
await asyncCounter();
await asyncCounter();
 
console.log("Script complete");
 
// with 1s between:
// Async Counter: 1
// Async Counter: 2
// Async Counter: 3
// immediately after counter 3:
// Script completeExample 3: Task scheduling with closures and async timing
This example highlights how closure captures the shared taskCount variable, which is incremented before each scheduled task.
All tasks reference the same taskCount value by the time their setTimeout callbacks run, resulting in each log displaying Task Count: 3.
It also demonstrates how microtasks (like Promise.resolve().then) run before timers and the event loop order between synchronous code, promises, and setTimeout.
function createTaskScheduler() {
  let taskCount = 0;
  return function scheduleTask() {
    taskCount++;
    setTimeout(() => {
      console.log("Task Count:", taskCount);
    }, taskCount * 1000);
  };
}
 
const scheduleTask = createTaskScheduler();
 
scheduleTask();
scheduleTask();
 
Promise.resolve().then(() => {
  console.log("promise");
  scheduleTask();
});
 
console.log("Tasks scheduled");
 
// Tasks scheduled
// promise
// after 1s with 1s between
// Task Count: 3
// Task Count: 3
// Task Count: 3Example 4: Scheduling tasks with preserved state using closures
In this version, the closure captures the taskCount value in a separate variable savedCount before scheduling the setTimeout.
This ensures that each task logs the correct value at the time it was created, rather than referencing the final shared taskCount.
The result is that each setTimeout logs a different value in increasing order, showcasing how to avoid timing issues by saving state early.
function createTaskScheduler() {
  let taskCount = 0;
  return function scheduleTask() {
    taskCount++;
    const savedCount = taskCount;
    setTimeout(() => {
      console.log("Task Count:", savedCount);
    }, taskCount * 1000);
  };
}
 
const scheduleTask = createTaskScheduler();
 
scheduleTask();
scheduleTask();
 
Promise.resolve().then(() => {
  scheduleTask();
});
 
console.log("Tasks scheduled");
 
// Tasks scheduled
// after 1s with 1s between:
// Task Count: 1
// Task Count: 2
// Task Count: 3Example 5: Preserving async state with closure in chained promises
This example demonstrates how closures can maintain and update state (count) across multiple asynchronous calls.
Because the promise is chained (then -> then), each counter() call executes sequentially, allowing count to increment between calls.
The use of setTimeout inside the promise simulates asynchronous work, and the closure ensures the count value is correctly preserved across executions.
function createCounter() {
  let count = 0;
  return function incrementCounter() {
    count++;
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(count);
      }, 1000);
    });
  };
}
 
const counter = createCounter();
 
// if it was called at the same time, count would
// have not been saved in closure
 
counter()
  .then((result) => {
    console.log("Counter 1:", result);
    return counter();
  })
  .then((result) => {
    console.log("Counter 2:", result);
  });
 
console.log("Script in progress");
 
// Script in progress
// after 1s with 1s between:
// Counter 1: 1
// Counter 2: 2Example 6: Asynchronous multiplier using closure and chained promises
This example showcases how closures can maintain internal state (factor = 2) in an asynchronous function.
The multiplyByTwo function, returned by createAsyncMultiplier, remembers the factor across multiple chained .then() calls.
Each multiplication is delayed with setTimeout, and the result of one multiplication is passed to the next, demonstrating sequential asynchronous logic with shared state.
function createAsyncMultiplier() {
  let factor = 2;
 
  return function multiply(value) {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(value * factor);
      }, 1000);
    });
  };
}
 
const multiplyByTwo = createAsyncMultiplier();
 
multiplyByTwo(5)
  .then((result) => {
    console.log("Multiply 1:", result);
    return multiplyByTwo(result);
  })
  .then((result) => {
    console.log("Multiply 2:", result);
    return multiplyByTwo(result);
  })
  .then((result) => {
    console.log("Multiply 3:", result);
  });
 
console.log("Multiplication started");
 
// Multiplication started
// after 1s with 1s between:
// Multiply 1: 10
// Multiply 2: 20
// Multiply 3: 40Reducer pattern with actions
This example demonstrates the use of a reducer function similar to the pattern used in React’s useReducer.
The reducer function handles different types of actions (added, changed, deleted) to manage a list of tasks.
It accumulates state changes over time as actions are applied via
Array.prototype.reduce,
making it a powerful pattern for predictable state updates in complex logic flows.
function tasksReducer(tasks, action) {
  switch (action.type) {
    case "added": {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case "changed": {
      return tasks.map((t) => {
        if (t.id === action.id) {
          const { type, ...actionNoType } = action;
          return actionNoType;
        } else {
          return t;
        }
      });
    }
    case "deleted": {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error("Unknown action: " + action.type);
    }
  }
}
 
const initialState = [];
const actions = [
  { type: "added", id: 1, text: "Visit Kafka Museum" },
  { type: "added", id: 2, text: "Watch a puppet show" },
  { type: "deleted", id: 1 },
  { type: "added", id: 3, text: "Lennon Wall pic" },
  { type: "changed", id: 3, text: "Lennon Wall", done: true },
];
const finalState = actions.reduce(tasksReducer, initialState);
console.log(finalState);
// [
//     { id: 2, text: 'Watch a puppet show', done: false },
//     { id: 3, text: 'Lennon Wall', done: true }
// ]Retry with exponential backoff
This snippet demonstrates how to implement a retry mechanism when fetching data from an API.
It attempts to fetch the resource multiple times (up to a given retries limit) and waits a specified delay between each retry.
If the request ultimately fails, it throws an error. This is particularly useful for handling flaky network requests
or ensuring robustness when dealing with unstable APIs.
async function fetchWithRetry(url, retries, delay = 1000) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP Error: ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error);
 
      if (attempt === retries) {
        throw new Error(`Failed to fetch after ${retries} retries`);
      }
 
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}
 
const res = await fetchWithRetry("https://pokeapi.co/api/v2/pokemon-color", 3);
console.log(res);Shuffle (Fisher-Yates Algorithm)
This example implements the Fisher-Yates shuffle, a reliable way to randomly shuffle elements in an array. The algorithm works by iterating the array from the end to the beginning, swapping the current element with a randomly selected one from earlier in the array (or itself). It ensures a uniform distribution of permutations.
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
 
    [array[i], array[j]] = [array[j], array[i]];
  }
}
 
const shuffledArray = [1, 2, 3, 4, 5];
shuffle(shuffledArray);
console.log(shuffledArray); // [ 2, 1, 4, 5, 3 ]Topological Sort
This example performs a topological sort on a set of items with dependencies. Each item can only appear in the result after all of its dependencies are resolved. It’s a common algorithm used in build systems, task schedulers, and dependency resolution tools.
const cards = [
  { id: 1, dependent: [6, 7, 8] },
  { id: 2, dependent: [6] },
  { id: 3, dependent: [] },
  { id: 4, dependent: [6, 7, 8] },
  { id: 5, dependent: [6, 8] },
  { id: 6, dependent: [] },
  { id: 7, dependent: [6] },
  { id: 8, dependent: [7] },
  { id: 9, dependent: [1] },
  { id: 10, dependent: [9] },
];
 
const getOrderedCards = (cards) => {
  const result = [];
  const added = new Set();
 
  while (result.length < cards.length) {
    for (const card of cards) {
      if (
        !added.has(card.id) &&
        card.dependent.every((dep) => added.has(dep))
      ) {
        result.push(card.id);
        added.add(card.id);
      }
    }
  }
 
  return result;
};
 
console.log(getOrderedCards(cards));
// [
//   3, 6, 7, 8,  1,
//   2, 4, 5, 9, 10
// ]