DEV Community

Muhammad Muhktar Musa
Muhammad Muhktar Musa

Posted on

The upside and downside of lodash

Lodash is a modern javaScript utility library that delivers modularity, performance and other task.

Upside

Lodash provides tools for making code cleaner and more functional. It supports modern browsing environments and helps in building modular applications. It makes working with arrays, numbers, objects and strings more easier. Lodash is also excellent for iterating arays, objects and strings as well as manipulating and testing values. Let us take a look at some lodash functions and how they improve functionality. We are going to compare them to the vanilla javaScript equivalent.
The aim is to understand how this functionalities are achieved under the hood.

sortBy

It creates an array of elements sorted in ascending order by the results of running each element in a collection through each iteratee. This method performs a stable sort. It also preserves the original sort order of equal elements. the iteratees are invoked with a single argument: (value). It returns the new sorted array.

Lodash

First we import the function from lodash

 import {sortBy} from 'lodash';
Enter fullscreen mode Exit fullscreen mode

Then we create an array of users

const users = [
  { 'user': 'fred', 'age': 48 },
  { 'user': 'brand', 'age': 36 },
  { 'user': 'fred', 'age': 40 },
  { 'user': 'brand', 'age': 34 }
];
Enter fullscreen mode Exit fullscreen mode

We now apply the the lodash sortBy function to the array

_.sortBy(users, function(o) { return o.user; });
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]

_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]

_.sortBy(users, 'user', function(o) {
  return Math.floor(o.age / 10);
});
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
Enter fullscreen mode Exit fullscreen mode

We get the above result.

Vanilla javaScript
const users = [
  { 'user': 'fred', 'age': 48 },
  { 'user': 'brand', 'age': 36 },
  { 'user': 'fred', 'age': 40 },
  { 'user': 'brand', 'age': 34 }
];
Enter fullscreen mode Exit fullscreen mode
const sortBy = (key) => {
  return (a, b) => (a[key] > b[key]) ? 1
   : ((b[key] > a[key]) ? -1 : 0);
};
Enter fullscreen mode Exit fullscreen mode

We now use the native sort to modify the array in place. We also use the concat() method to copy the array before sorting.

users.concat().sort(sortBy('user'));
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]];

users.concat().sort(sortBy('user', 'age'));
// => objects for [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]];
Enter fullscreen mode Exit fullscreen mode

Curry

Creates a function that accepts arguments of func and either invokes func returning its result if at least the number of arguments is specified or returns a function that accepts the remaining func arguments. The arguments of the func maybe specified if func.length is not sufficient

Lodash
import { curry } from 'lodash';
const abc = (a, b, c) => [a, b, c];
const curried = curry(abc);
curried(3)(4)(5);
Enter fullscreen mode Exit fullscreen mode

For the code solution above check
https://dev.to/mcube25/javascript-advanced-functions-for-dummies-4i6g:
check for more info on currying

Vanilla javaScript

First we define the number of expected arguments

const curry = func => {
  const expectedArgs = func.length;
  const curried = (...args) => {

  }
}
Enter fullscreen mode Exit fullscreen mode

If enough arguments have been passed, we return the result of the function execution or else we continue adding to the list

const curry = func => {
  const expectedArgs = func.length;
  const curried = (...args) => {
    return args.length >= expectedArgs
      ? func(...args)
      : (...args2) => curried(...args.concat(args2))
  }
  return curried
}
Enter fullscreen mode Exit fullscreen mode

For the code solution above check
https://dev.to/mcube25/javascript-advanced-functions-for-dummies-4i6g:
check for more info on currying

Chunk

It creates an array of elements split into groups the length of the size. The size will be the second argument provided to the function.

Lodash

Import chunk from lodash

import { chunk } from 'lodash';
chunk(['boy', 'girl', 'brother', 'sister', 'uncle', 'aunt'], 2);
//=>[['boy', 'girl'], ['brother', 'sister'], [uncle', 'aunt']];
Enter fullscreen mode Exit fullscreen mode

If collection can’t be split evenly, the final chunk will be the remaining elements.
Example

chunk(['boy', 'girl', 'brother', 'sister', 'uncle', 'aunt', 'father'], 2);
//=>[['boy', 'girl'], ['brother', 'sister'], [uncle', 'aunt'], [father]];
Enter fullscreen mode Exit fullscreen mode
Vanilla javaScript

Using plain javaScript the lodash example can be written as

const chunk = (arr, arrSize, cache = []) => {
  const temp = [...arr]
  if (arrSize <= 0) { return cache }
  while (temp.length) {
    cache.push(temp.splice(0, arrSize))
    return cache
  }
}
chunk(['boy', 'girl', 'brother', 'sister', 'uncle', 'aunt'], 2);
//=>[['boy', 'girl'], ['brother', 'sister'], [uncle', 'aunt']];
Enter fullscreen mode Exit fullscreen mode

pullAt

This function removes elements from an array corresponding to indexes and returns an array of removed elements. This method mutates the array

Lodash
import { pullAt } from 'lodash';
const array = ['2', '3', '4', '5', '6'];
pullAt(array, [2, 4]);
//=>[4, 6]
Enter fullscreen mode Exit fullscreen mode
Vanilla javaScript
const pullAt = (arr, idxs) => {
  idxs.reverse().map(
    idx => arr.splice(idx, 1)[0]
  ).reverse()
};

pullAt(array, [2, 4]);
//=>[4, 6]
Enter fullscreen mode Exit fullscreen mode

Remove

This function removes all elements from an array that affirms or denies the subject in the proposition logic. It returns truthy for the array. It also returns an array of the removed elements. It is invoked with three arguments which are (value, index, array). It mutates the array

Lodash
import { remove } from 'lodash';
const array = [1, 2, 3, 4, 5];
const even = remove(array, n => {
  return n % 2 === 0
});

// the array //=>[1, 3, 5];
// even //=> [2, 4];
Enter fullscreen mode Exit fullscreen mode
Vanilla javaScript
const remove = (array, ix) => {
  const toRemove = [];
  const result = array.filter((item, i) =>
    ix(item) && toRemove.push(i)
  )
};
Enter fullscreen mode Exit fullscreen mode

In order to not mutate the original array until the very end, we want to cache the indexes while preparing the result to return in the code above. Just before returning, we can remove the items making sure to start from the higher indexes to prevent them shifting at each removal.

const remove = (array, ix) => {
  const toRemove = [];
  const result = array.filter((item, i) =>
    ix(item) && toRemove.push(i)
  )
  toRemove.reverse().forEach(i => array.splice(i, 1))
  return result
}
const array = [1, 2, 3, 4, 5];
const even = remove(array, n => {
  return n % 2 === 0
});

// the array //=>[1, 3, 5];
// even //=> [2, 4];
Enter fullscreen mode Exit fullscreen mode

Reduce

This function reduces a collection to a value that is the accumulated result of running each element in the collection through an iteratee. Each successive invocation is supplied the return value of the previous. If the accumulator is not given, then the first element of the collection is used as the initial value. The iteratee is invoked with four arguments: (accumulator, value, index, collection)

Lodash
import { reduce } from 'lodash';

reduce([3, 4], (sum, n) => sum + n, 0);
//=>7 
Enter fullscreen mode Exit fullscreen mode
Vanilla javaScript
array = [3, 4];
array.reduce((sum, n) => sum + n, 0);
//=>7 
Enter fullscreen mode Exit fullscreen mode

Before

It creates a function that invokes a func with the this binding and arguments of the created function while it is called less than n times. Subsequent calls to the created function returns the result of the last func invocation

Lodash
import { before } from 'lodash';

(t, fn) => before(t, fn);
Enter fullscreen mode Exit fullscreen mode
Vanilla javaScript
const before = (t, fn) => {
  let counter = 0;
  let res;
  return (...args) => {
    counter++;
    if (counter <= t) {
      res = fn(...args);
      return res
    } else {
      return res
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Downside

As can be seen, the amount of code written when using lodash is minimal compared to using plain javaScript. But the downside of using lodash in an application is that it increases the size of the application which in turn affects the performance of such applications.

Discussion (2)

Collapse
larsonnn profile image
Lars Feldeisen

Core build (~4kB gzipped)
Full build (~24kB gzipped)

Many functions are now available in JavaScript, but loadash is still a valid tool for many things, like deep cloning objects etc.

here a full list: lodash.com/docs/4.17.15

But nice comparison too vanilla.

Collapse
peerreynders profile image
peerreynders

like deep cloning objects

Coming soon: MDN: structuredClone()

And with the Node.js V8 serialization API:

import { deserialize, serialize } from 'v8';

const structuredClone = value => deserialize(serialize(value));
Enter fullscreen mode Exit fullscreen mode