Implementation of Lodash functions

chunk

The _.chunk() function in Lodash splits an array into smaller arrays, or "chunks," of a specified size. It returns a new array of chunks, making it easier to manage large datasets by dividing them into smaller, more manageable parts.

This example demonstrates a custom implementation of Lodash's _.chunk() function to achieve the same result.

const chunk = (inputArray, quantity) => {
  return inputArray.reduce((resultArray, item, index) => {
    const chunkIndex = Math.floor(index / quantity);
 
    if (!resultArray[chunkIndex]) {
      resultArray[chunkIndex] = []; // start a new chunk
    }
 
    resultArray[chunkIndex].push(item);
 
    return resultArray;
  }, []);
};
 
const perChunk = 2; // items per chunk
const inputArray = ["a", "b", "c", "d", "e"];
 
console.log(chunk(inputArray, perChunk));
// [['a','b'], ['c','d'], ['e']]

compact

The _.compact() function in Lodash creates an array by removing all falsey values from the provided array. Falsey values include false, null, 0, "", undefined, and NaN. This is useful for cleaning up arrays and removing unwanted, invalid values.

This example shows a custom implementation of Lodash's _.compact() function to filter out falsey values from the input array.

const compact = (inputArray) => inputArray.filter(Boolean);
 
const inputArray = ["a", false, null, 0, "", undefined, NaN, "d"];
console.log(compact(inputArray)); // [ 'a', 'd' ]

curry

The _.curry() function in Lodash transforms a function into a curried version that can be called with a series of partial arguments. Instead of calling the function with all arguments at once, you can call it progressively, one argument at a time.

This example shows a custom implementation of currying for the sum function, allowing it to be invoked with one argument at a time or multiple arguments in a sequence.

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return (...nextArgs) => curried(...args, ...nextArgs);
    }
  };
}
 
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
 
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6

difference

The _.difference() method returns the elements from the first array that are not present in any of the other arrays provided. It computes the difference by using the Set data structure for efficient lookups.

This custom implementation compares two arrays and finds the elements unique to each array, returning the differences in two parts: one for elements in arr1 but not in arr2, and one for elements in arr2 but not in arr1.

const findDifference = function (arr1, arr2) {
  const set1 = new Set(arr1);
  const set2 = new Set(arr2);
 
  const diffLeft = [];
  const diffRight = [];
 
  for (const item of set1) {
    if (!set2.has(item)) diffLeft.push(item);
  }
 
  for (const item of set2) {
    if (!set1.has(item)) diffRight.push(item);
  }
 
  return [diffLeft, diffRight];
};
 
const array1 = [2, 1, 3];
const array2 = [2, 3];
const result = findDifference(array1, array2);
console.log(result); // [ [ 1 ], [] ]

differenceBy

The _.differenceBy() method works like difference(), but allows you to compare elements by a specific property or transformation function. You pass a key or iteratee to compare the objects by their properties.

In this custom implementation, we compare objects in arr1 and arr2 by a key (x), and return the differences based on that key.

const differenceBy = (arr1, arr2, key) => {
  const set2 = new Set(arr2.map((item) => item[key]));
  const set1 = new Set(arr1.map((item) => item[key]));
 
  const diffLeft = [];
  const diffRight = [];
 
  for (const item of arr1) {
    if (!set2.has(item[key])) {
      diffLeft.push(item);
    }
  }
 
  for (const item of arr2) {
    if (!set1.has(item[key])) {
      diffRight.push(item);
    }
  }
 
  return [diffLeft, diffRight];
};
 
const array1 = [{ x: 2 }, { x: 1 }, { x: 1 }];
const array2 = [{ x: 1 }];
const result = differenceBy(array1, array2, "x");
console.log(result); // [ [ { x: 2 } ], [] ]

intersection

The _.intersection() method computes the intersection of two or more arrays. It returns a new array containing the elements that are present in all provided arrays.

This custom implementation compares two arrays by converting them to Set objects for efficient lookup and finding the common elements between them.

const intersection = function (nums1, nums2) {
  const set1 = new Set(nums1);
  const set2 = new Set(nums2);
  const result = [];
 
  for (const nums of set2) {
    if (set1.has(nums)) {
      result.push(nums);
    }
  }
 
  return result;
};
 
console.log(intersection([2, 1], [2, 3])); // [ 2 ]

keyBy

The _.keyBy() method creates an object composed of keys generated from the results of running each element in the collection through an iteratee function. The iteratee can be a function or property name.

This custom implementation maps each item in the collection to a key determined by the provided iteratee, and then assigns the item to that key in the result object.

function keyBy(collection, iteratee) {
  const result = {};
 
  for (const item of collection) {
    const key =
      typeof iteratee === "function" ? iteratee(item) : item[iteratee];
    result[key] = item;
  }
 
  return result;
}
 
const array = [
  { dir: "left", code: 97 },
  { dir: "right", code: 100 },
];
 
const res1 = keyBy(array, ({ code }) => String.fromCharCode(code));
console.log(res1);
// { a: { dir: 'left', code: 97 }, d: { dir: 'right', code: 100 } }
 
const res2 = keyBy(array, "dir");
console.log(res2);
// { left: { dir: 'left', code: 97 }, right: { dir: 'right', code: 100 } }

omit

The _.omit() method creates a shallow copy of an object without the specified properties. The properties to omit can be provided as a single key or an array of keys.

This custom implementation removes the given properties from the object by first creating a copy and then deleting the specified keys.

function omit(obj, keys) {
  const result = { ...obj };
 
  if (!Array.isArray(keys)) {
    delete result[keys];
    return result;
  }
 
  for (const key of keys) {
    delete result[key];
  }
 
  return result;
}
 
const user = {
  name: "Alice",
  age: 25,
  email: "alice@example.com",
  city: "Wonderland",
};
 
const omitted = omit(user, "age");
console.log(omitted);
// { name: 'Alice', email: 'alice@example.com', city: 'Wonderland' }
 
const omitted2 = omit(user, ["age", "email"]);
console.log(omitted2);
// { name: 'Alice', city: 'Wonderland' }

orderBy

The _.orderBy() method sorts an array of objects based on one or more properties. You can specify the sort order for each property as either ascending ("asc") or descending ("desc").

This custom implementation replicates the _.orderBy() method by accepting an array, a property to sort by, and an optional order parameter ("asc" by default). The array is first copied to avoid mutation, then sorted based on the specified property and order.

function orderBy(array, property, order = "asc") {
  const multiplier = order === "asc" ? 1 : -1;
  const copy = [...array];
 
  return copy.sort((a, b) => {
    if (a[property] < b[property]) return -1 * multiplier;
    if (a[property] > b[property]) return 1 * multiplier;
    return 0;
  });
}
 
const users = [
  { user: "barney", age: 36 },
  { user: "fred", age: 40 },
  { user: "pebbles", age: 1 },
];
 
const sortedByAgeAsc = orderBy(users, "age", "asc");
console.log(sortedByAgeAsc);
// [
//   { user: 'pebbles', age: 1 },
//   { user: 'barney', age: 36 },
//   { user: 'fred', age: 40 }
// ]
 
const sortedByUserDesc = orderBy(users, "user", "desc");
console.log(sortedByUserDesc);
// [
//   { user: 'pebbles', age: 1 },
//   { user: 'fred', age: 40 },
//   { user: 'barney', age: 36 }
// ]

partition

The _.partition() method splits an array into two groups based on a predicate function. The first group contains elements for which the predicate returns truthy, and the second group contains elements for which the predicate returns falsey. The predicate function is invoked with one argument: the value of the element.

This custom implementation mimics the _.partition() method by using the reduce() function to iterate over the array and push elements into two arrays based on the result of the predicate.

function partition(array, predicate) {
  return array.reduce(
    (result, element) => {
      result[predicate(element) ? 0 : 1].push(element);
      return result;
    },
    [[], []],
  );
}
 
const users = [
  { user: "barney", age: 36, active: false },
  { user: "fred", age: 40, active: true },
  { user: "pebbles", age: 1, active: false },
];
 
const [active, inactive] = partition(users, (user) => user.active);
console.log(active);
// [{ 'user': 'fred', 'age': 40, 'active': true }]
console.log(inactive);
// [
//   { user: 'barney', age: 36, active: false },
//   { user: 'pebbles', age: 1, active: false }
// ]

pick

The _.pick() method creates a new object by picking the specified properties from an existing object. It accepts either a single key or an array of keys to extract.

This custom implementation mimics the _.pick() method by using reduce() to iterate over the array of keys and select only the properties that exist in the source object, returning a new object with the selected properties.

function pick(obj, keys) {
  if (typeof keys === "string") {
    return obj[keys] !== undefined ? { [keys]: obj[keys] } : {};
  }
 
  return (Array.isArray(keys) ? keys : []).reduce((result, key) => {
    if (key in obj) {
      result[key] = obj[key];
    }
    return result;
  }, {});
}
 
const user = {
  name: "Alice",
  age: 25,
  email: "alice@example.com",
  city: "Wonderland",
};
 
const picked = pick(user, ["name", "email"]);
console.log(picked);
// { name: 'Alice', email: 'alice@example.com' }

union

The _.union() method creates a new array of unique values by combining all given arrays, preserving the order of elements.

This custom implementation utilizes the Set object to remove duplicates and concat() to merge all arrays before applying Array.from() to convert the set back into an array.

const union = (...arrays) => {
  return Array.from(new Set([].concat(...arrays)));
};
 
console.log(union([1, 2], [1, 7, 7], [3, 1]));
// [ 1, 2, 7, 3 ]