Javascript Native Methods And Data Structures

at

The at() method takes an integer and returns the item at that index — supporting both positive and negative integers. Negative values count from the end of the array, making it cleaner than using array.length - index.

This example shows a custom implementation of Array.prototype.at() to mimic the native behavior.

Array.prototype.customAt = function (index) {
  if (index < 0) {
    index = this.length + index;
  }
 
  return this[index];
};
 
const array1 = [5, 12, 8, 130, 44];
console.log(array1.customAt(-1)); // 44

concat

The concat() method merges two or more arrays and returns a new array without modifying the original. It also handles non-array values by appending them as-is. This custom implementation replicates native behavior by flattening one level of array input while preserving nested structures.

Array.prototype.myConcat = function (...arrays) {
  const result = [...this];
 
  for (const array of arrays) {
    if (Array.isArray(array)) {
      result.push(...array);
    } else {
      result.push(array);
    }
  }
 
  return result;
};
 
const arr = [1, 2, 3];
const arr2 = [4, 5, 6, [1]];
const arr3 = [7, 8, 9];
const concat = arr.myConcat(arr2, arr3, 10);
console.log(concat);
// [1, 2, 3, 4, 5, 6, [1], 7, 8, 9, 10]

entries

The entries() method returns a new array iterator object containing [index, value] pairs for each element. It’s commonly used in for...of loops to iterate with both index and value.

This example recreates Array.prototype.entries() using a generator to yield key/value pairs.

Array.prototype.customEntries = function () {
  const entries = [];
  for (let i = 0; i < this.length; i++) {
    entries.push([i, this[i]]);
  }
 
  function* iterator() {
    yield* entries;
  }
 
  return iterator();
};
 
const arr = [{ x: "a" }, "b", ["c"]];
const iterator = arr.customEntries();
 
console.log(iterator.next());
// { value: [ 0, { x: 'a' } ], done: false }
console.log(iterator.next());
// { value: [ 1, 'b' ], done: false }
console.log(iterator.next());
// { value: [ 2, [ 'c' ] ], done: false }
console.log(iterator.next());
// { value: undefined, done: true }
 
for (const [index, element] of arr.customEntries()) {
  console.log(index, element);
}
// 0 { x: 'a' }
// 1 b
// 2 [ 'c' ]

every

The every() method tests whether all elements in an array pass the test implemented by the provided callback. It returns a booleantrue if all elements satisfy the condition, and false otherwise.

This example includes a custom implementation of Array.prototype.every() and shows how it can be used for subset checks.

Array.prototype.customEvery = function (callback) {
  for (let i = 0; i < this.length; i++) {
    if (!callback(this[i], i)) {
      return false;
    }
  }
 
  return true;
};
 
const arr = [1, 30, 39, 29, 10, 13];
 
console.log(arr.customEvery((currentValue) => currentValue > 40));
// false
console.log(arr.customEvery((currentValue) => currentValue < 40));
// true
 
const isSubset = (array1, array2) =>
  array2.customEvery((element) => array1.includes(element));
 
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 7, 6])); // true
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 8, 7])); // false

fill

The fill() method changes all elements in an array between the specified start and end indices to a static value, in place. It returns the modified array and is useful for quickly initializing or resetting arrays.

This example includes a custom implementation of Array.prototype.fill() with support for negative indices.

Array.prototype.customFill = function (value, start = 0, end = this.length) {
  if (start < 0) {
    start = this.length + start;
  }
 
  if (end < 0) {
    end = this.length + end;
  }
 
  for (let i = start; i < Math.min(end, this.length); i++) {
    this[i] = value;
  }
 
  return this;
};
 
const array1 = [1, 2, 3, 4];
 
// Fill with 0 from position 2 until position 4
console.log(array1.customFill(0, 2, 4));
// [1, 2, 0, 0]
 
// Fill with 5 from position 1
console.log(array1.customFill(5, 1));
// [1, 5, 5, 5]
 
console.log(array1.customFill(6));
// [6, 6, 6, 6]
 
console.log([1, 2, 3].customFill(4)); // [4, 4, 4]
console.log([1, 2, 3].customFill(4, 1)); // [1, 4, 4]
console.log([1, 2, 3].customFill(4, 1, 2)); // [1, 4, 3]
console.log([1, 2, 3].customFill(4, 1, 1)); // [1, 2, 3]
console.log([1, 2, 3].customFill(4, 3, 3)); // [1, 2, 3]
console.log([1, 2, 3].customFill(4, -3, -2)); // [4, 2, 3]

filter

The filter() method creates a shallow copy of an array, including only elements that pass the test implemented by the provided callback function. It’s ideal for narrowing down data based on conditions, like filtering by length, type, or custom logic.

This example includes a custom implementation of Array.prototype.filter() and practical use cases like filtering long words or prime numbers.

Array.prototype.myFilter = function (callback) {
  const result = [];
 
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this)) {
      result.push(this[i]);
    }
  }
 
  return result;
};
 
const words = ["spray", "elite", "exuberant", "destruction", "present"];
 
const result = words.myFilter((word) => word.length > 6);
 
console.log(result);
// ["exuberant", "destruction", "present"]
 
const array = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
 
function isPrime(num) {
  for (let i = 2; num > i; i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return num > 1;
}
 
console.log(array.myFilter(isPrime)); // [ 2, 3, 5, 7 ]

find

The find() method returns the first element in the array that satisfies the provided testing function. If no match is found, it returns undefined. Ideal for retrieving an item based on a condition without iterating the entire array manually.

Array.prototype.customFind = function (callback) {
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i])) {
      return this[i];
    }
  }
};
 
const array1 = [5, 12, 8, 130, 44];
const found = array1.customFind((element) => element > 10);
console.log(found); // 12
 
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];
 
const result = inventory.customFind(({ name }) => name === "cherries");
console.log(result); // { name: 'cherries', quantity: 5 }
 
const result1 = inventory.customFind(({ name }) => name === "nothing");
console.log(result1); // undefined

findIndex

The findIndex() method returns the index of the first element that passes the provided test function. If no element matches, it returns -1. Useful for locating the position of an item instead of the item itself.

Array.prototype.customFindIndex = function (callback) {
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i])) {
      return i;
    }
  }
 
  return -1;
};
 
const array1 = [5, 12, 8, 130, 44];
const found = array1.customFindIndex((element) => element > 10);
console.log(found); // 1
 
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];
 
const result = inventory.customFindIndex(({ name }) => name === "cherries");
console.log(result); // 2
 
const result1 = inventory.customFindIndex(({ name }) => name === "nothing");
console.log(result1); // -1

findLast

The findLast() method iterates the array in reverse and returns the first value that satisfies the given condition. If no element matches, undefined is returned. Useful when you want the last matching item in an array without reversing it manually.

Array.prototype.customFindLast = function (callback) {
  for (let i = this.length; i >= 0; i--) {
    if (callback(this[i])) {
      return this[i];
    }
  }
};
 
const array1 = [5, 12, 8, 130, 44];
const found = array1.customFindLast((element) => element > 10);
console.log(found); // 44

flatMap

The flatMap() method first maps each element using a given callback function, then flattens the result by one level. It behaves like array.map(...).flat(), but is more efficient and expressive for nested transformations.

This example demonstrates both native usage and a custom implementation of Array.prototype.flatMap() using a real-world data structure.

Array.prototype.customFlatMap = function (callback, thisArg) {
  const result = [];
 
  for (let i = 0; i < this.length; i++) {
    const mapped = callback.call(thisArg, this[i], i, this);
 
    if (Array.isArray(mapped)) {
      result.push(...mapped); // Use spread operator for flattening
    } else {
      result.push(mapped);
    }
  }
 
  return result;
};
 
const data = [
  {
    id: 1,
    name: "Category A",
    items: [
      {
        id: 2,
        name: "Subcategory A1",
      },
      {
        id: 5,
        name: "Subcategory A2",
      },
    ],
  },
  {
    id: 8,
    name: "Category B",
    items: [
      {
        id: 9,
        name: "Subcategory B1",
      },
      {
        id: 12,
        name: "Subcategory B2",
      },
    ],
  },
];
 
const items = data
  .map((category) =>
    category.items.map((item) => ({
      ...item,
      category: category.name,
    })),
  )
  .flat();
console.log(items);
// [
//   { id: 2, name: 'Subcategory A1', category: 'Category A' },
//   { id: 5, name: 'Subcategory A2', category: 'Category A' },
//   { id: 9, name: 'Subcategory B1', category: 'Category B' },
//   { id: 12, name: 'Subcategory B2', category: 'Category B' }
// ]
 
const items2 = data.customFlatMap((category) =>
  category.items.map((item) => ({
    ...item,
    category: category.name,
  })),
);
console.log(items2);
// ...same result

flat

The flat() method creates a new array with all sub-array elements recursively flattened up to the specified depth. It's useful for simplifying nested arrays into a single-level array — or flattening deeply nested structures with Infinity.

This example provides a custom implementation of Array.prototype.flat(), supporting variable depth.

Array.prototype.customFlat = function (depth = 1) {
  const result = [];
 
  const flatten = (array, depth) => {
    for (const item of array) {
      if (Array.isArray(item) && depth > 0) {
        flatten(item, depth - 1);
      } else {
        result.push(item);
      }
    }
  };
  flatten(this, depth);
 
  return result;
};
 
const arr = [0, 1, [2, [3, [4, 5]]]];
console.log(arr.customFlat());
// [0, 1, 2, [3, [4, 5]]]
console.log(arr.customFlat(2));
// [0, 1, 2, 3, [4, 5]]
console.log(arr.customFlat(Infinity));
// [0, 1, 2, 3, 4, 5]

includes

The includes() method checks if an array contains a specific value, returning true or false. It uses the SameValueZero equality comparison — meaning it treats NaN as equal to NaN.

This example includes a custom implementation of Array.prototype.includes() with support for optional fromIndex and negative indexing behavior.

function sameValueZero(x, y) {
  return (
    x === y ||
    (typeof x === "number" && typeof y === "number" && x !== x && y !== y)
  );
}
 
Array.prototype.customIncludes = function (searchElement, fromIndex = 0) {
  const length = this.length;
 
  if (length === 0) {
    return false;
  }
 
  if (fromIndex < 0) {
    fromIndex = Math.max(length + fromIndex, 0);
  }
 
  for (let i = fromIndex; i < length; i++) {
    if (sameValueZero(this[i], searchElement)) {
      return true;
    }
  }
 
  return false;
};
 
console.log([1, 2, 3].customIncludes(2)); // true
console.log([1, 2, 3].customIncludes(4)); // false
console.log([1, 2, 3].customIncludes(3, 3)); // false
console.log([1, 2, 3].customIncludes(3, -1)); // true
console.log([1, 2, NaN].customIncludes(NaN)); // true
console.log(["1", "2", "3"].customIncludes(3)); // false
 
const arr = ["a", "b", "c"];
// Since -100 is much less than the array length,
// it starts checking from index 0.
console.log(arr.customIncludes("a", -100)); // true
console.log(arr.customIncludes("a", -2)); // false
console.log(arr.customIncludes("a", -3)); // true

indexOf

The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present. It uses strict equality (===) for comparison and supports an optional fromIndex to control the search starting point.

This custom implementation of Array.prototype.indexOf() also handles negative indexing by adjusting the start position.

Array.prototype.customIndexOf = function (searchElement, fromIndex = 0) {
  if (fromIndex < 0) {
    fromIndex = this.length + fromIndex;
  }
 
  for (let i = fromIndex; i < this.length; i++) {
    if (this[i] === searchElement) {
      return i;
    }
  }
 
  return -1;
};
 
const beasts = ["ant", "bison", "camel", "duck", "bison"];
console.log(beasts.customIndexOf("bison")); // 1
// Start from index 2
console.log(beasts.customIndexOf("bison", 2)); // 4
console.log(beasts.customIndexOf("giraffe")); // -1
 
const array = [2, 9, 9];
console.log(array.customIndexOf(9, 2)); // 2
console.log(array.customIndexOf(2, -1)); // -1
console.log(array.customIndexOf(2, -3)); // 0
console.log(array.customIndexOf(2, -100)); // 0

join

The join() method creates and returns a string by concatenating all elements of an array, separated by a specified string (default is a comma). If the array has only one item, that item is returned as-is. It’s useful for formatting output or combining array values into CSV-like strings.

This example includes a custom implementation of Array.prototype.join() that replicates the native behavior.

Array.prototype.customJoin = function (separator = ",") {
  let result = "";
 
  for (let i = 0; i < this.length; i++) {
    if (i > 0) {
      result += separator;
    }
 
    result += this[i];
  }
 
  return result;
};
 
const elements = ["Fire", "Air", "Water"];
console.log(elements.customJoin()); // "Fire,Air,Water"
console.log(elements.customJoin("")); // "FireAirWater"
console.log(elements.customJoin("-")); // "Fire-Air-Water"
 
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];
console.log(matrix.customJoin()); // 1,2,3,4,5,6,7,8,9
console.log(matrix.customJoin(";")); // 1,2,3;4,5,6;7,8,9

Map

The Map object holds key-value pairs and remembers the original insertion order of the keys. Keys can be any value, including objects, functions, and NaN. Unlike objects, maps offer consistent key ordering and better performance for frequent additions and deletions.

Instance methods:

Instance properties:

// Creating a Map and adding entries
const map = new Map();
map.set("name", "Alice");
map.set("age", 25);
map.set("country", "USA");
console.log(map);
// Map(3) { 'name' => 'Alice', 'age' => 25, 'country' => 'USA' }
 
// Size
console.log(map.size); // 3
 
// Accessing values
console.log(map.get("name")); // Alice
 
// Checking for a key
console.log(map.has("age")); // true
 
// Removing a key
map.delete("country");
console.log(map);
// Map(2) { 'name' => 'Alice', 'age' => 25 }
 
map.set("city", "New York");
map.set("hobby", "Painting");
 
// Using for...of
console.log("Using for...of:");
for (const [key, value] of map) {
  console.log(key, ":", value);
}
 
// Getting all keys, values, or entries
console.log("Keys:", [...map.keys()]);
// Keys: [ 'name', 'age', 'city', 'hobby' ]
console.log("Values:", [...map.values()]);
// Values: [ 'Alice', 25, 'New York', 'Painting' ]
console.log("Entries:", [...map.entries()]);
// Entries: [
//     [ 'name', 'Alice' ],
//     [ 'age', 25 ],
//     [ 'city', 'New York' ],
//     [ 'hobby', 'Painting' ]
// ]
 
// Clearing the map
map.clear();
console.log(map); // Map(0) {}
 
// Creating a Map with an array of entries
const mapFromArray = new Map([
  ["fruit", "Apple"],
  ["color", "Red"],
  ["quantity", 10],
]);
console.log(mapFromArray);
// Map(3) { 'fruit' => 'Apple', 'color' => 'Red', 'quantity' => 10 }
 
// Converting a Map back to an object
const obj = Object.fromEntries(mapFromArray);
console.log(obj);
// { fruit: 'Apple', color: 'Red', quantity: 10 }
 
// Map vs Object comparison
const objExample = { a: 1, b: 2, c: 3 };
const mapExample = new Map(Object.entries(objExample));
console.log(mapExample);
// Map(3) { 'a' => 1, 'b' => 2, 'c' => 3 }
 
// Keys of different types
const map2 = new Map();
const keyObj = {};
const keyFunc = function () {};
 
map2.set("hello", "string value");
map2.set(keyObj, "obj value");
map2.set(keyFunc, "func value");
map2.set(NaN, "NaN value");
 
console.log(map2);
// Map(4) {
//   'hello' => 'string value',
//   {} => 'obj value',
//   [Function: keyFunc] => 'func value',
//   NaN => 'NaN value'
// }
 
console.log(map2.get("hello")); // string value
console.log(map2.get(keyObj)); // obj value
console.log(map2.get(keyFunc)); // func value
console.log(map2.get(NaN)); // NaN value

WeakMap

The WeakMap object is a special kind of map whose keys must be objects, and those keys are weakly referenced — meaning they do not prevent garbage collection.

This makes WeakMap useful for storing metadata or private data tied to object lifetimes. However, it does not support iteration or methods like .keys() or .clear().

If an object has lost all other references, then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens

let john = { name: "John" };
 
const map = new Map();
map.set(john, "...");
 
john = null;
console.log(map.get(john)); // undefined
console.log(map.has(john)); // false
console.log(map.keys());
// [Map Iterator] { { name: 'John' } }
 
let doe = { name: "Doe" };
const weakMap = new WeakMap();
weakMap.set(doe, "...");
 
doe = null;
console.log(weakMap.get(doe)); // undefined
// weakMap.keys() not supported

Map.groupBy

The Map.groupBy() static method groups elements from an iterable based on the return value of a callback function. It returns a new Map where each key is the grouping criterion and each value is an array of items in that group.

This example provides a polyfill-style custom implementation of Map.groupBy() and demonstrates grouping numbers, objects by property, and even using objects as keys for advanced use cases.

// Map.groupBy isn't available yet
function groupBy(array, callback) {
  const map = new Map();
 
  for (const item of array) {
    const key = callback(item);
    const group = map.get(key) || [];
    group.push(item);
    map.set(key, group);
  }
 
  return map;
}
 
// Using Map.groupBy to group numbers by even and odd
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 
const groupedNumbers = groupBy(numbers, (num) =>
  num % 2 === 0 ? "even" : "odd",
);
console.log(groupedNumbers);
// Map(2) {
//   'odd' => [1, 3, 5, 7, 9],
//   'even' => [2, 4, 6, 8]
// }
 
// Grouping people by their age group
const people = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 },
  { name: "Charlie", age: 25 },
  { name: "David", age: 35 },
];
 
const groupedByAge = groupBy(people, (person) => person.age);
console.log(groupedByAge);
// Map(3) {
//   25 => [ { name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 } ],
//   30 => [ { name: 'Bob', age: 30 } ],
//   35 => [ { name: 'David', age: 35 } ]
// }
 
const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 9 },
  { name: "bananas", type: "fruit", quantity: 5 },
  { name: "goat", type: "meat", quantity: 23 },
  { name: "cherries", type: "fruit", quantity: 12 },
  { name: "fish", type: "meat", quantity: 22 },
];
 
// Using objects as keys
const restock = { restock: true };
const sufficient = { restock: false };
const result = groupBy(inventory, ({ quantity }) =>
  quantity < 6 ? restock : sufficient,
);
console.log(result);
// Map(2) {
//     { restock: false } => [
//         { name: 'asparagus', type: 'vegetables', quantity: 9 },
//         { name: 'goat', type: 'meat', quantity: 23 },
//         { name: 'cherries', type: 'fruit', quantity: 12 },
//         { name: 'fish', type: 'meat', quantity: 22 }
//     ],
//         { restock: true } => [ { name: 'bananas', type: 'fruit', quantity: 5 } ]
// }
console.log(result.get(restock));
// [{ name: "bananas", type: "fruit", quantity: 5 }]

map

The map() method creates a new array populated with the results of calling a provided function on every element in the original array. It's a key tool for transforming data structures in a clean, functional style — especially for UI rendering, data formatting, or calculations.

This example includes a custom implementation of Array.prototype.map() along with common use cases like doubling values, reformatting objects, and calculating cumulative sums.

Array.prototype.customMap = function (callbackFn) {
  if (typeof callbackFn !== "function") {
    throw new TypeError("Callback must be a function");
  }
 
  const arr = [];
  for (let i = 0; i < this.length; i++) {
    arr.push(callbackFn(this[i], i, this));
  }
 
  return arr;
};
 
const array1 = [1, 4, 9, 16];
const doubled = array1.customMap((x) => x * 2);
console.log(doubled); // [ 2, 8, 18, 32 ]
 
const kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 },
];
const formattedArray = kvArray.customMap(({ key, value }) => ({
  [key]: value,
}));
console.log(formattedArray); // [ { 1: 10 }, { 2: 20 }, { 3: 30 } ]
 
console.log(["1", "2", "3"].customMap(Number)); // [ 1, 2, 3 ]
 
const numbers2 = [1, 2, 3, 4, 5];
const cumulativeSum = numbers2.customMap((num, idx, arr) => {
  // Calculate cumulative sum by adding the current number to the sum of previous numbers
  const previousSum =
    idx > 0 ? arr.slice(0, idx).reduce((acc, curr) => acc + curr, 0) : 0;
  return previousSum + num;
});
console.log(cumulativeSum); // [ 1, 3, 6, 10, 15 ]

Object.assign

The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target, making it useful for shallow cloning or merging multiple objects.

This example also shows how Object.assign() behaves similarly to the spread operator (...) when cloning simple objects.

const obj = {
  foo: 1,
  get bar() {
    return 2;
  },
};
 
let copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 }
// The value of copy.bar is obj.bar's getter's return value.
 
// Following 2 lines of code are the same.
const objClone = { ...obj }; // { foo: 1, bar: 2 }
const objClone2 = Object.assign({}, obj); // { foo: 1, bar: 2 }

Object.groupBy

The Object.groupBy() static method groups elements of an iterable based on the string key returned by a callback function. It returns a plain object with keys representing group names and values as arrays of grouped items.

This example shows a manual implementation using Array.prototype.reduce() to group inventory items by their type field.

const groupBy = (arr, callback) => {
  return arr.reduce((acc = {}, item) => {
    const key = callback(item);
    if (!acc[key]) acc[key] = [];
    acc[key].push(item);
 
    return acc;
  }, {});
};
 
const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 5 },
  { name: "bananas", type: "fruit", quantity: 0 },
  { name: "goat", type: "meat", quantity: 23 },
  { name: "cherries", type: "fruit", quantity: 5 },
  { name: "fish", type: "meat", quantity: 22 },
];
const result = groupBy(inventory, ({ type }) => type);
console.log(result);
 
// {
//   vegetables: [{ name: "asparagus", type: "vegetables", quantity: 5 }],
//   fruit: [
//     { name: "bananas", type: "fruit", quantity: 0 },
//     { name: "cherries", type: "fruit", quantity: 5 },
//   ],
//   meat: [
//     { name: "goat", type: "meat", quantity: 23 },
//     { name: "fish", type: "meat", quantity: 22 },
//   ],
// };

pop

The pop() method removes the last element from an array and returns it. It mutates the original array by reducing its length by one.

This example provides a custom implementation of Array.prototype.pop() that replicates native behavior.

Array.prototype.customPop = function () {
  const length = this.length;
 
  if (length === 0) {
    return undefined;
  }
 
  const lastElement = this[length - 1];
  this.length = length - 1;
 
  return lastElement;
};
 
const fruits = ["Apple", "Banana", "Cherry"];
const lastFruit = fruits.customPop();
console.log(lastFruit); // Cherry
console.log(fruits); // [ "Apple", "Banana" ]

Promise.allSettled

The Promise.allSettled() method returns a promise that resolves after all promises in the input iterable have settled, regardless of whether they were fulfilled or rejected. Each result is an object with a status of either "fulfilled" or "rejected" and contains either the value or the reason.

This example demonstrates a custom implementation of Promise.allSettled() using .then() and .catch() handlers for consistent result formatting.

const rejectHandler = (reason) => ({ status: "rejected", reason });
const resolveHandler = (value) => ({ status: "fulfilled", value });
 
Promise.customAllSettled = function (promises) {
  const convertedPromises = promises.map((p) =>
    Promise.resolve(p).then(resolveHandler, rejectHandler),
  );
 
  return Promise.all(convertedPromises);
};
 
Promise.customAllSettled([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) =>
    setTimeout(() => reject(new Error("Whoops!")), 2000),
  ),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
])
  .then(console.info)
  .catch(console.error);
// [
//   { status: 'fulfilled', value: 1 },
//   {
//     status: 'rejected',
//     reason: Error: Whoops!...
// },
// { status: 'fulfilled', value: 3 }
// ]

Promise.all

The Promise.all() static method takes an iterable of promises and returns a single Promise that:

This example includes a custom implementation of Promise.all() that resolves all promises and preserves their order, even if they complete at different times.

function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError("Argument must be an array"));
    }
 
    const results = [];
    let completedPromises = 0;
 
    for (let index = 0; index < promises.length; index++) {
      Promise.resolve(promises[index])
        .then((value) => {
          results[index] = value;
          console.log(value);
          completedPromises += 1;
          if (completedPromises === promises.length) {
            resolve(results);
          }
        })
        .catch(reject);
    }
 
    if (promises.length === 0) {
      resolve([]);
    }
  });
}
 
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, "first");
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "second");
});
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 5000, "third");
});
 
myPromiseAll([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
 
// second
// first
// third
// [ 'first', 'second', 'third' ]

Promise.any

The Promise.any() method returns a promise that fulfills as soon as any of the input promises is fulfilled. If all promises reject, it rejects with an AggregateError containing all rejection reasons.

This example demonstrates a custom implementation of Promise.any() that resolves with the first successful result or throws an aggregate error if none succeed.

Promise.customAny = function (promises) {
  return new Promise((resolve, reject) => {
    const errors = [];
    let remaining = promises.length;
 
    if (remaining === 0) {
      return reject(new AggregateError([], "All promises were rejected"));
    }
 
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(resolve)
        .catch((error) => {
          errors[index] = error;
          remaining -= 1;
          if (remaining === 0) {
            reject(new AggregateError(errors, "All promises were rejected"));
          }
        });
    });
  });
};
 
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "quick"));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, "slow"));
 
const promises = [promise1, promise2, promise3];
 
Promise.customAny(promises).then((value) => console.log(value)); // quick

Promise.race

The Promise.race() method returns a promise that settles as soon as the first input promise settles — whether it fulfills or rejects. This makes it useful when you're interested only in the fastest outcome, regardless of its result.

This example includes a custom implementation of Promise.race() that resolves or rejects based on whichever promise finishes first.

Promise.customRace = function (promises) {
  return new Promise((resolve, reject) => {
    for (const promise of promises) {
      Promise.resolve(promise).then(resolve, reject);
    }
  });
};
 
Promise.customRace([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) =>
    setTimeout(() => reject(new Error("Whoops!")), 2000),
  ),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(console.log); // 1

push

The push() method adds one or more elements to the end of an array and returns the new length of the array. It's a common and efficient way to grow an array in-place.

This example demonstrates a custom implementation of Array.prototype.push() that handles multiple arguments and mimics native behavior.

Array.prototype.customPush = function () {
  for (let i = 0; i < arguments.length; i++) {
    this[this.length] = arguments[i];
  }
 
  return this.length;
};
 
const animals = ["pigs", "goats"];
 
const count = animals.customPush("cows");
console.log(count); // 3
 
console.log(animals); // [ 'pigs', 'goats', 'cows' ]
 
animals.customPush("chickens", "cats");
console.log(animals);
// [ 'pigs', 'goats', 'cows', 'chickens', 'cats' ]

reduce

The reduce() method executes a user-defined "reducer" callback function on each element of the array, in order. It accumulates a single value by applying the callback function and passing the result from one iteration to the next.

The method can accept an optional initial value, and if not provided, it defaults to the first element in the array. This custom implementation mimics the native behavior.

This example demonstrates usage of reduce for both synchronous and asynchronous operations, including practical examples like summing an array and composing a pipeline of functions.

Array.prototype.customReduce = function (callback, initialValue) {
  let accumulator = initialValue !== undefined ? initialValue : this[0];
 
  const startIndex = initialValue !== undefined ? 0 : 1;
 
  for (let i = startIndex; i < this.length; i++) {
    accumulator = callback(accumulator, this[i], i, this);
  }
 
  return accumulator;
};
 
const array1 = [15, 16, 17, 18, 19];
 
const sumWithInitial = array1.customReduce((acc, cur) => acc + cur, 0);
console.log(sumWithInitial); // 85
 
const sumWithoutInitial = array1.customReduce((acc, cur) => acc + cur);
console.log(sumWithoutInitial); // 85
 
const pipe =
  (...functions) =>
  (initialValue) =>
    functions.customReduce((acc, fn) => fn(acc), initialValue);
 
const double = (x) => x * 2;
const triple = (x) => x * 3;
 
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
 
console.log(multiply6(6)); // 36
console.log(multiply9(9)); // 81
 
const asyncPipe =
  (...functions) =>
  (initialValue) =>
    functions.reduce((acc, fn) => acc.then(fn), Promise.resolve(initialValue));
 
const p1 = async (a) => a * 5;
const p2 = async (a) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(a * 2), 1000);
  });
const f3 = (a) => a * 3;
const p4 = async (a) => a * 4;
 
asyncPipe(p1, p2, f3, p4)(10).then(console.log); // 1200
 
const asyncPipeAsync =
  (...functions) =>
  (initialValue) =>
    functions.reduce(async (acc, fn) => fn(await acc), initialValue);
 
asyncPipeAsync(p1, p2, f3, p4)(10).then(console.log); // 1200

reverse

The reverse() method reverses an array in place and returns the modified array. The first element becomes the last, and the last becomes the first, effectively reversing the array's order.

This custom implementation of Array.prototype.reverse() reverses the array elements by swapping them in place.

Array.prototype.customReverse = function () {
  const middle = Math.floor(this.length / 2);
 
  for (let i = 0; i < middle; i++) {
    const temp = this[i];
    this[i] = this[this.length - 1 - i];
    this[this.length - 1 - i] = temp;
  }
 
  return this;
};
 
const array1 = ["one", "two", "three", "four"];
 
const reversed = array1.customReverse();
console.log(reversed); // [ 'four', 'three', 'two', 'one' ]
// reverse changes the original array
console.log(array1); // [ 'four', 'three', 'two', 'one' ]

Set

The Set object lets you store unique values of any type, whether primitive or object references. A Set automatically removes duplicate elements and maintains the insertion order.

This example demonstrates how to create a set, add/remove elements, check membership, iterate through a set, and perform common set operations like union, intersection, and difference.

// Create a Set
const mySet = new Set([1, 2, 3, 4, 5]);
console.log(mySet); // Set { 1, 2, 3, 4, 5 }
 
// returns a new set iterator object that contains the values
// for each element in this set in insertion order
const setValues = mySet.values();
console.log(setValues);
// [Set Iterator] { 1, 2, 3, 4, 5 }
console.log(setValues.next()); // { value: 1, done: false }
 
// Add elements to a Set
mySet.add(6); // Adds a new element
mySet.add(2); // Duplicate values are ignored
console.log(mySet); // Set { 1, 2, 3, 4, 5, 6 }
 
// Check if a Set contains an element
console.log(mySet.has(3)); // true
console.log(mySet.has(10)); // false
 
// Remove elements from a Set
mySet.delete(4); // Removes the element 4
console.log(mySet); // Set { 1, 2, 3, 5, 6 }
 
// Get the size of a Set
console.log(mySet.size); // 5
 
// Iterate over a Set
for (const value of mySet) {
  console.log(value); // Logs each value in the Set
}
 
// Convert a Set to an Array
const arrayFromSet = Array.from(mySet); // Using Array.from()
console.log(arrayFromSet); // [1, 2, 3, 5, 6]
 
const anotherArray = [...mySet]; // Using spread operator
console.log(anotherArray); // [1, 2, 3, 5, 6]
 
// Clear a Set
mySet.clear(); // Removes all elements
console.log(mySet); // Set {}
 
// Find the Union of Two Sets
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
// Combines elements from both sets
const union = new Set([...setA, ...setB]);
console.log(union); // Set { 1, 2, 3, 4, 5 }
 
// Find the Intersection of Two Sets
// Common elements
const intersection = new Set([...setA].filter((x) => setB.has(x)));
console.log(intersection); // Set { 3 }
 
// Find the Difference of Two Sets
// Elements in setA not in setB
const difference = new Set([...setA].filter((x) => !setB.has(x)));
console.log(difference); // Set { 1, 2 }
 
const a = {};
const b = {};
const c = {};
const setOfObjects = new Set([a, a, b]);
console.log(setOfObjects); // Set(2) { {}, {} }
console.log(setOfObjects.has(a)); // true
console.log(setOfObjects.has(c)); // false

shift

The shift() method removes the first element from an array and returns it, shifting the remaining elements down. This method mutates the array by reducing its length.

This custom implementation of Array.prototype.shift() manually shifts the array elements and returns the removed first element.

Array.prototype.customShift = function () {
  if (!this.length) return;
 
  const firstElement = this[0];
 
  for (let i = 0; i < this.length; i++) {
    this[i] = this[i + 1];
  }
 
  this.length -= 1;
 
  return firstElement;
};
 
const array1 = [1, 2, 3];
 
const firstElement = array1.customShift();
 
console.log(array1); // [2, 3]
console.log(firstElement); // 1

slice

The slice() method returns a shallow copy of a portion of an array into a new array, without modifying the original array. The selection is made from start index to end index (excluding the end element).

This custom implementation of Array.prototype.slice() replicates the native behavior, with support for negative indexing.

Array.prototype.customSlice = function (start = 0, end) {
  const length = this.length;
  let endIndex = end || length;
 
  if (start < 0) {
    start = Math.max(length + start, 0);
  }
  if (endIndex < 0) {
    endIndex = Math.max(length + endIndex, 0);
  }
 
  const result = [];
 
  for (let i = start; i < endIndex && i < length; i++) {
    result.push(this[i]);
  }
 
  return result;
};
 
const animals = ["ant", "bison", "camel", "duck", "elephant"];
 
console.log(animals.customSlice(2));
// ["camel", "duck", "elephant"]
console.log(animals.customSlice(2, 4)); // ["camel", "duck"]
console.log(animals.customSlice(-2)); // ["duck", "elephant"]
console.log(animals.customSlice(2, -1)); // ["camel", "duck"]
console.log(animals.customSlice());
// ["ant", "bison", "camel", "duck", "elephant"]

some

The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if any element satisfies the condition; otherwise, it returns false. This method does not modify the original array.

This custom implementation of Array.prototype.some() iterates through the array and returns true if any element passes the provided test.

Array.prototype.customSome = function (callback) {
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this)) {
      return true;
    }
  }
 
  return false;
};
 
const array = [1, 2, 3, 4, 5];
 
const even = (element) => element % 2 === 0;
console.log(array.customSome(even)); // true
 
const equal90 = (element) => element === 90;
console.log(array.customSome(equal90)); // false

sort

The sort() method sorts the elements of an array in place, changing the array's order and returning the reference to the sorted array. By default, the sort order is ascending, and it compares elements as strings based on their UTF-16 code unit values.

This custom implementation of Array.prototype.sort() uses the bubble sort algorithm and accepts an optional compare function to define the sorting criteria.

Array.prototype.customSort = function (compareFn) {
  if (typeof compareFn !== "function") {
    compareFn = (a, b) => (String(a) > String(b) ? 1 : -1);
  }
 
  // bubble sort
  let n = this.length;
  for (let i = 0; i < n - 1; i++) {
    let swapped = false;
    for (let j = 0; j < n - i - 1; j++) {
      if (compareFn(this[j], this[j + 1]) > 0) {
        // Swap using destructuring
        [this[j], this[j + 1]] = [this[j + 1], this[j]];
        swapped = true;
      }
    }
    // If no two elements were swapped, the array is sorted
    if (!swapped) break;
  }
 
  return this;
};
 
const months = ["March", "Jan", "Feb", "Dec"];
months.customSort();
console.log(months); // ["Dec", "Feb", "Jan", "March"]
 
const array1 = ["1", "30", "4", "21", "1000"];
array1.customSort();
console.log(array1); // ["1", "1000", "21", "30", "4"]
 
const array2 = [1, 30, 4, 21, 1000];
array2.customSort();
console.log(array2); // [1, 1000, 21, 30, 4]
 
const array3 = [5, 8, 100, 0];
array3.customSort((a, b) => a - b);
console.log(array3); // [0, 5, 8, 100]

splice

The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.

This custom implementation of Array.prototype.splice() handles negative indices, normalization of deleteCount, and array modification in place, while returning the removed elements.

Array.prototype.customSplice = function (
  startIndex,
  deleteCount,
  ...itemsToAdd
) {
  const length = this.length;
 
  // Handle negative indices
  startIndex =
    startIndex < 0
      ? Math.max(length + startIndex, 0)
      : Math.min(startIndex, length);
 
  // If deleteCount is undefined, remove all elements starting from startIndex
  if (deleteCount === undefined) {
    deleteCount = length - startIndex;
  } else {
    // Normalize deleteCount
    deleteCount = Math.max(0, Math.min(deleteCount, length - startIndex));
  }
 
  // Extract the array to be deleted
  const splicedItems = this.slice(startIndex, startIndex + deleteCount);
 
  // Create the resulting this by combining parts and items to add
  const remainingItems = [
    ...this.slice(0, startIndex),
    ...itemsToAdd,
    ...this.slice(startIndex + deleteCount),
  ];
 
  // Update the original array
  this.length = 0; // Clear the this
  for (let i = 0; i < remainingItems.length; i++) {
    this[i] = remainingItems[i];
  }
 
  // Return the deleted items
  return splicedItems;
};
 
// Remove 0 (zero) elements before index 2, and insert "drum"
const myFish = ["angel", "clown", "mandarin", "sturgeon"];
const removed = myFish.customSplice(2, 0, "drum");
console.log({ myFish, removed });
// myFish: [ 'angel', 'clown', 'drum', 'mandarin', 'sturgeon' ]
// removed: []
 
// Remove 0 (zero) elements before index 2, and insert "drum" and "guitar"
const myFish1 = ["angel", "clown", "mandarin", "sturgeon"];
const removed1 = myFish1.customSplice(2, 0, "drum", "guitar");
console.log({ myFish1, removed1 });
// myFish1: [ 'angel', 'clown', 'drum', 'guitar', 'mandarin', 'sturgeon' ],
// removed1: []
 
// Remove 0 (zero) elements at index 0, and insert "angel"
// splice(0, 0, ...elements) inserts elements at the start of
// the array like unshift().
const myFish2 = ["clown", "mandarin", "sturgeon"];
const removed2 = myFish2.customSplice(0, 0, "angel");
console.log({ myFish2, removed2 });
// myFish2: [ 'angel', 'clown', 'mandarin', 'sturgeon' ], removed2: []
 
// Remove 0 (zero) elements at last index, and insert "sturgeon"
// splice(array.length, 0, ...elements) inserts elements at the
// end of the array like push().
const myFish3 = ["angel", "clown", "mandarin"];
const removed3 = myFish3.customSplice(myFish.length, 0, "sturgeon");
console.log({ myFish3, removed3 });
// myFish3: [ 'angel', 'clown', 'mandarin', 'sturgeon' ], removed3: []
 
// Remove 1 element at index 3
const myFish4 = ["angel", "clown", "drum", "mandarin", "sturgeon"];
const removed4 = myFish4.customSplice(3, 1);
console.log({ myFish4, removed4 });
// myFish4: [ 'angel', 'clown', 'drum', 'sturgeon' ],
// removed4: [ 'mandarin' ]
 
// Remove 1 element at index 2, and insert "trumpet"
const myFish5 = ["angel", "clown", "drum", "sturgeon"];
const removed5 = myFish5.customSplice(2, 1, "trumpet");
console.log({ myFish5, removed5 });
// myFish5: [ 'angel', 'clown', 'trumpet', 'sturgeon' ],
// removed5: [ 'drum' ]
 
// Remove 2 elements from index 0, and insert "parrot", "anemone" and "blue"
const myFish6 = ["angel", "clown", "trumpet", "sturgeon"];
const removed6 = myFish6.customSplice(0, 2, "parrot", "anemone", "blue");
console.log({ myFish6, removed6 });
// myFish6: [ 'parrot', 'anemone', 'blue', 'trumpet', 'sturgeon' ],
// removed6: [ 'angel', 'clown' ]
 
// Remove 2 elements, starting from index 2
const myFish7 = ["parrot", "anemone", "blue", "trumpet", "sturgeon"];
const removed7 = myFish7.customSplice(2, 2);
console.log({ myFish7, removed7 });
// myFish7: [ 'parrot', 'anemone', 'sturgeon' ],
// removed7: [ 'blue', 'trumpet' ]
 
// Remove 1 element from index -2
const myFish8 = ["angel", "clown", "mandarin", "sturgeon"];
const removed8 = myFish8.customSplice(-2, 1);
console.log({ myFish8, removed8 });
// myFish8: [ 'angel', 'clown', 'sturgeon' ], removed8: [ 'mandarin' ]
 
// Remove all elements, starting from index 2
const myFish9 = ["angel", "clown", "mandarin", "sturgeon"];
const removed9 = myFish9.customSplice(2);
console.log({ myFish9, removed9 });
// myFish9: [ 'angel', 'clown' ], removed9: [ 'mandarin', 'sturgeon' ]

toSorted

The toSorted() method is a copying version of the sort() method. It returns a new array with the elements sorted in ascending order.

This custom implementation mimics the behavior of toSorted() by creating a shallow copy of the original array and performing a sort operation on it without modifying the original array.

Array.prototype.customSort = function (compareFn) {
  if (typeof compareFn !== "function") {
    compareFn = (a, b) => (String(a) > String(b) ? 1 : -1);
  }
 
  const copy = [...this];
 
  // bubble sort
  const n = this.length;
  for (let i = 0; i < n - 1; i++) {
    let swapped = false;
    for (let j = 0; j < n - i - 1; j++) {
      if (compareFn(copy[j], copy[j + 1]) > 0) {
        // Swap using destructuring
        [copy[j], copy[j + 1]] = [copy[j + 1], copy[j]];
        swapped = true;
      }
    }
    // If no two elements were swapped, the array is sorted
    if (!swapped) break;
  }
 
  return copy;
};
 
const months = ["March", "Jan", "Feb", "Dec"];
console.log(months.customSort());
// ["Dec", "Feb", "Jan", "March"]
 
const array1 = ["1", "30", "4", "21", "1000"];
console.log(array1.customSort());
// ["1", "1000", "21", "30", "4"]
 
const array2 = [1, 30, 4, 21, 1000];
console.log(array2.customSort());
// [1, 1000, 21, 30, 4]
 
const array3 = [5, 8, 100, 0];
console.log(array3.customSort((a, b) => a - b));
// [0, 5, 8, 100]

unshift

The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.

In this custom implementation, the method first shifts all existing elements to the right to make space for the new elements. Then, it inserts the new elements at the beginning of the array and returns the updated length.

Array.prototype.unshift = function (...elements) {
  const originalLength = this.length;
  const totalLength = elements.length + originalLength;
 
  // Shift existing elements to the right
  for (let i = originalLength - 1; i >= 0; i--) {
    this[i + elements.length] = this[i];
  }
 
  // Add new elements at the beginning
  for (let i = 0; i < elements.length; i++) {
    this[i] = elements[i];
  }
 
  return totalLength; // Return the new length
};
 
const arr = [3, 4, 5];
arr.unshift(1, 2);
console.log(arr); // [1, 2, 3, 4, 5]
console.log(arr.length); // 5