• Get application security done the right way! Detect, Protect, Monitor, Accelerate, and more…
  • For JavaScript developers, Lodash needs no introduction. However, the library is vast and often feels overwhelming. Not any more!

    Lodash, Lodash, Lodash . . . where do I even start! 🤔

    There was a time when the JavaScript ecosystem was nascent; it could be compared to the wild west or a jungle if you will, where a lot was going on, but there were very few answers for everyday developer frustrations and productivity.

    Then Lodash entered the scene, and it felt like a flood that submerged everything. Right from simple everyday needs like sorting to complex data structure transformations, Lodash came loaded (overloaded, even!) with functionality that turned JS devs’ life into sheer bliss.

    Hello, Lodash!

    And where is Lodash today? Well, it still has all the goodies it offered initially, and then some, but it seems to have lost mind share in the JavaScript community. Why? I can think of a few reasons:

    • Some functions in the Lodash library were (and still are) slow when applied to large lists. While this would’ve never affected 95% of the projects out there, influential devs from the remaining 5% gave Lodash a bad press and the effect cascaded down into the grassroots.
    • There’s a trend in the JS ecosystem (might even say the same thing about the Golang folks) where hubris is more common than necessary. So, relying on something like Lodash is seen as stupid and gets shot down on forums like StackOverflow when people suggest such solutions (“What?! Use an entire library for something like this? I can combine filter() with reduce() to achieve the same thing in a simple function!”).
    • Lodash is old. At least by JS standards. It came out in 2012, so as of writing, it’s been almost ten years. The API has been stable, and not much exciting stuff can be added every year (simply because there’s no need to), which generates boredom for the average overexcited JS developer.

    In my opinion, not using Lodash is a significant loss for our JavaScript codebases. It has proven bug-free and elegant solutions for everyday problems we run into at work, and using it will only make our code more readable and maintainable.

    With that said, let’s dive into some of the common (or not!) Lodash functions and see just how incredibly helpful and beautiful this library is.

    Clone . . . deeply!

    Since objects are passed by reference in JavaScript, it creates a headache for developers when wanting to clone something with the hope that the new data set is different.

    let people = [
      {
        name: 'Arnold',
        specialization: 'C++',
      },
      {
        name: 'Phil',
        specialization: 'Python',
      },
      {
        name: 'Percy',
        specialization: 'JS',
      },
    ];
    
    // Find people writing in C++
    let folksDoingCpp = people.filter((person) => person.specialization == 'C++');
    
    // Convert them to JS!
    for (person of folksDoingCpp) {
      person.specialization = 'JS';
    }
    
    console.log(folksDoingCpp);
    // [ { name: 'Arnold', specialization: 'JS' } ]
    
    console.log(people);
    /*
    [
      { name: 'Arnold', specialization: 'JS' },
      { name: 'Phil', specialization: 'Python' },
      { name: 'Percy', specialization: 'JS' }
    ]
    */
    

    Notice how in our pure innocence and despite our good intentions, the original people array mutated in the process (Arnold’s specialization changed from C++ to JS) — a major blow to the integrity of the underlying software system! Indeed, we need a way to make a true (deep) copy of the original array.

    Hi Dave, meet Dave!

    You can perhaps argue that this is a “silly” way of coding in JS; however, the reality is a bit complicated. Yes, we have the lovely destructuring operator available, but anyone who has tried to destructure complex objects and arrays knows the pain. Then, there’s the idea of using serialization and de-serialization (perhaps JSON) to achieve deep copying, but it only makes your code messier for the reader.

    By contrast, look how amazingly elegant and concise is the solution when Lodash gets used:

    const _ = require('lodash');
    
    let people = [
      {
        name: 'Arnold',
        specialization: 'C++',
      },
      {
        name: 'Phil',
        specialization: 'Python',
      },
      {
        name: 'Percy',
        specialization: 'JS',
      },
    ];
    
    let peopleCopy = _.cloneDeep(people);
    
    // Find people writing in C++
    let folksDoingCpp = peopleCopy.filter(
      (person) => person.specialization == 'C++'
    );
    
    // Convert them to JS!
    for (person of folksDoingCpp) {
      person.specialization = 'JS';
    }
    
    console.log(folksDoingCpp);
    // [ { name: 'Arnold', specialization: 'JS' } ]
    
    console.log(people);
    /*
    [
      { name: 'Arnold', specialization: 'C++' },
      { name: 'Phil', specialization: 'Python' },
      { name: 'Percy', specialization: 'JS' }
    ]
    */
    

    Notice how the people array is untouched after deep cloning (Arnold still specializes in C++ in this case). But more importantly, the code is straightforward to understand.

    Remove duplicates from an array

    Removing duplicates from an array sounds like an excellent interview/whiteboarding problem (remember, when in doubt, throw a hashmap at the problem!). And, of course, you can always write a custom function to do that, but what if you encounter several different scenarios in which to make your arrays unique? You could write several other functions for that (and risk running into subtle bugs), or you could just use Lodash!

    Our first example of unique arrays is rather trivial, but it still represents the speed and reliability that Lodash brings to the table. Imagine doing this by writing all the custom logic yourself!

    const _ = require('lodash');
    
    const userIds = [12, 13, 14, 12, 5, 34, 11, 12];
    const uniqueUserIds = _.uniq(userIds);
    console.log(uniqueUserIds);
    // [ 12, 13, 14, 5, 34, 11 ]
    

    Notice that the final array is not sorted, which of course, isn’t of any concern here. But now, let’s imagine a more complicated scenario: we have an array of users we pulled from somewhere, but we want to make sure it contains only unique users. Easy with Lodash!

    const _ = require('lodash');
    
    const users = [
      { id: 10, name: 'Phil', age: 32 },
      { id: 8, name: 'Jason', age: 44 },
      { id: 11, name: 'Rye', age: 28 },
      { id: 10, name: 'Phil', age: 32 },
    ];
    
    const uniqueUsers = _.uniqBy(users, 'id');
    console.log(uniqueUsers);
    /*
    [
      { id: 10, name: 'Phil', age: 32 },
      { id: 8, name: 'Jason', age: 44 },
      { id: 11, name: 'Rye', age: 28 }
    ]
    */

    In this example, we used the uniqBy() method to tell Lodash that we want the objects to be unique on the id property. In one line, we expressed what could’ve taken 10-20 lines and introduced more scope for bugs!

    There’s a lot more stuff available around making things unique in Lodash, and I encourage you to have a look at the docs.

    Difference of two arrays

    Union, difference, etc., might sound like terms best left behind in dull high school lectures on Set Theory, but they pop up more often than not in everyday practice. It’s common to have a list and want to merge another list with it or wanting to find which elements are unique to it as compared to another list; for these scenarios, the difference function is perfect.

    Hello, A. Bye-bye, B!

    Let’s begin the journey of difference by taking a simple scenario: you’ve received a list of all the user ids in the system, as well as a list of those whose accounts are active. How do you find the inactive ids? Simple, right?

    const _ = require('lodash');
    
    const allUserIds = [1, 3, 4, 2, 10, 22, 11, 8];
    const activeUserIds = [1, 4, 22, 11, 8];
    
    const inactiveUserIds = _.difference(allUserIds, activeUserIds);
    console.log(inactiveUserIds);
    // [ 3, 2, 10 ]
    

    And what if, as it happens in a more realistic setting, you have to work with an array of objects instead of plain primitives? Well, Lodash has a nice differenceBy() method for this!

    const allUsers = [
      { id: 1, name: 'Phil' },
      { id: 2, name: 'John' },
      { id: 3, name: 'Rogg' },
    ];
    const activeUsers = [
      { id: 1, name: 'Phil' },
      { id: 2, name: 'John' },
    ];
    const inactiveUsers = _.differenceBy(allUsers, activeUsers, 'id');
    console.log(inactiveUsers);
    // [ { id: 3, name: 'Rogg' } ]

    Neat, right?!

    Like difference, there are other methods in Lodash for common set operations: union, intersection, etc.

    Flattening arrays

    The need to flatten arrays arises quite often. One use case is that you’ve received an API response and need to apply some map() and filter()combo on a complex list of nested objects/arrays to pluck out, say, user ids, and now you’re left with arrays of arrays. Here’s a code snippet depicting this situation:

    const orderData = {
      internal: [
        { userId: 1, date: '2021-09-09', amount: 230.0, type: 'prepaid' },
        { userId: 2, date: '2021-07-07', amount: 130.0, type: 'prepaid' },
      ],
      external: [
        { userId: 3, date: '2021-08-08', amount: 30.0, type: 'postpaid' },
        { userId: 4, date: '2021-06-06', amount: 330.0, type: 'postpaid' },
      ],
    };
    
    // find user ids that placed postpaid orders (internal or external)
    const postpaidUserIds = [];
    
    for (const [orderType, orders] of Object.entries(orderData)) {
      postpaidUserIds.push(orders.filter((order) => order.type === 'postpaid'));
    }
    console.log(postpaidUserIds);

    Can you guess what postPaidUserIds now looks like? Hint: it’s disgusting!

    [
      [],
      [
        { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
        { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
      ]
    ]

    Now, if you’re a sensible person, you don’t want to write custom logic to extract the order objects and lay them out nicely in a row inside an array. Just use the flatten() method and enjoy the grapes:

    const flatUserIds = _.flatten(postpaidUserIds);
    console.log(flatUserIds);
    /*
    [
      { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
      { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
    ]
    */

    Do note that flatten() only goes one level deep. That is if your objects are stuck two, three, or more levels deep, flatten() they will disappoint you. In those cases, Lodash has the flattenDeep() method, but do be warned that applying this method on very large structures can slow things down (as behind the scenes, there’s a recursive operation at work).

    Is the object/array empty?

    Thanks to how “falsy” values and types work in JavaScript, sometimes something as simple as checking for emptiness results in existential dread.

    How do you check if an array is empty? You can check whether its length is 0 or not. Now, how do you check if an object is empty? Well…wait a minute! This is where that uneasy feeling sets in, and those JavaScript examples containing stuff like [] == false and {} == false start circling our heads. When under pressure to deliver a feature, landmines like these are the last thing you need — they will make your code hard to understand, and they will introduce uncertainty in your test suite.

    Working with missing data

    In the real world, data listen to us; no matter how badly we want it, it’s rarely streamlined and sane. One typical example is missing null objects/arrays in a large data structure received as API response.

    Suppose we received the following object as an API response:

    const apiResponse = {
      id: 33467,
      paymentRefernce: 'AEE3356T68',
      // `order` object missing
      processedAt: `2021-10-10 00:00:00`,
    };

    As shown, we generally get an order Object in the response from the API, but it’s not always the case. So, what if we have some code that relies on this object? One way would be to code defensively, but depending on how nested the order object is, we’d soon be writing very ugly code if we wish to avoid runtime errors:

    if (
      apiResponse.order &&
      apiResponse.order.payee &&
      apiResponse.order.payee.address
    ) {
      console.log(
        'The order was sent to the zip code: ' +
          apiResponse.order.payee.address.zipCode
      );
    }

    🤢🤢 Yup, very ugly to write, very ugly to read, very ugly to maintain, and so on. Thankfully, Lodash has a straightforward way of dealing with such situations.

    const zipCode = _.get(apiResponse, 'order.payee.address.zipCode');
    console.log('The order was sent to the zip code: ' + zipCode);
    // The order was sent to the zip code: undefined

    There’s also the fantastic option of providing a default value instead of getting undefined for missing stuff:

    const zipCode2 = _.get(apiResponse, 'order.payee.address.zipCode', 'NA');
    console.log('The order was sent to the zip code: ' + zipCode2);
    // The order was sent to the zip code: NA

    I don’t know about you, but get() is one of those things that bring tears of happiness to my eyes. It’s not anything flashy. There’s no consulted syntax or options to memorize, yet look at the amount of collective suffering it can alleviate! 😇

    Debouncing

    In case you’re unfamiliar, debouncing is a common theme in frontend development. The idea is that sometimes it’s beneficial to launch an action not immediately but after some time (generally, a few milliseconds). What does that mean? Here’s an example.

    Imagine an e-commerce website with a search bar (well, any website/web app these days!). For better UX, we don’t want the user to have to hit enter (or worse, press the “search” button) to show suggestions/previews based on their search term. But the obvious answer is a bit loaded: if we add an event listener to onChange() for the search bar and fire off an API call for every keystroke, we’d have created a nightmare for our backend; there would be too many unnecessary calls (for example, if “white carpet brush” is searched, there will be a total of 18 requests!) and almost all of these will be irrelevant because the user input hasn’t finished.

    The answer lies in debouncing, and the idea is this: do not send an API call as soon as the text changes. Wait for some time (say, 200 milliseconds) and if by that time there’s another keystroke, cancel the earlier time count and again start waiting. As a result, it’s only when the user pauses (either because they’re thinking or because they’re done and they expect some response) that we send an API request to the backend.

    The overall strategy I described is complicated, and I won’t dive into the synchronization of timer management and cancellation; however, the actual debouncing process is very simple if you’re using Lodash.

    const _ = require('lodash');
    const axios = require('axios');
    
    // This is a real dogs' API, by the way!
    const fetchDogBreeds = () =>
      axios
        .get('https://dog.ceo/api/breeds/list/all')
        .then((res) => console.log(res.data));
    
    const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 1000); // after one second
    debouncedFetchDogBreeds(); // shows data after some time
    

    If you’re thinking setTimeout() I would’ve done the same job, well, there’s more! Lodash’s debounce comes with many powerful features; for example, you might want to ensure that the debounce isn’t indefinite. That is, even if there’s a keystroke every time the function is about to fire off (thus canceling the overall process), you might want to make sure that the API call is made anyway after, say, two seconds. For this, Lodash debounce() has the maxWait option:

    const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 150, { maxWait: 2000 }); // debounce for 250ms, but send the API request after 2 seconds anyway

    Do check out the official docs for a deeper dive. They’re full of super-important stuff!

    Remove values from an array

    I don’t know about you, but I hate to write code for removing items from an array. First, I have to get the item’s index, check whether the index is actually valid and if so, call the splice() method, and so on. I can never remember the syntax and thus needs to look things up all the time, and at the end of it, I am left with the nagging feeling that I’ve let some stupid bug creep in.

    const greetings = ['hello', 'hi', 'hey', 'wave', 'hi'];
    _.pull(greetings, 'wave', 'hi');
    console.log(greetings);
    // [ 'hello', 'hey' ]

    Please note two things:

    1. The original array got changed in the process.
    2. The pull() method removes all instances, even if there are duplicates.

    There’s another related method called pullAll() that accepts an array as the second parameter, making it easier to remove multiple items at once. Granted that we could just use pull() with a spread operator, but remember that Lodash came at a time when the spread operator wasn’t even a proposal in the language!

    const greetings2 = ['hello', 'hi', 'hey', 'wave', 'hi'];
    _.pullAll(greetings2, ['wave', 'hi']);
    console.log(greetings2);
    // [ 'hello', 'hey' ]

    Last index of an element

    JavsScript’s native indexOf() method is cool, except when you’re interested in scanning the array from the opposite direction! And yet again, yes, you could just write a decrementing loop and find the element, but why not use a lot more elegant technique?

    Here’s a quick Lodash solution using the lastIndexOf() method:

    const integers = [2, 4, 1, 6, -1, 10, 3, -1, 7];
    const index = _.lastIndexOf(integers, -1);
    console.log(index); // 7

    Unfortunately, there’s no variant of this method where we can look up complex objects or even pass a custom lookup function.

    Zip. Unzip!

    Unless you’ve worked in Python, zip/unzip is a utility you may never notice or imagine in your entire career as a JavaScript developer. And perhaps for a good reason: there’s rarely the kind of desperate need for zip/unzip as there is for filter(), etc. However, it’s one of the best lesser-known utilities around and can help you create succinct code in some situations.

    Contrary to what it sounds like, zip/unzip has nothing to do with compression. Instead, it’s a grouping operation where arrays of the same length can be converted into a single array of arrays with elements at the same position packed together (zip()) and back (unzip()). Yeah, I know, it’s getting hazy trying to make do with words, so let’s look at some code:

    const animals = ['duck', 'sheep'];
    const sizes = ['small', 'large'];
    const weight = ['less', 'more'];
    
    const groupedAnimals = _.zip(animals, sizes, weight);
    console.log(groupedAnimals);
    // [ [ 'duck', 'small', 'less' ], [ 'sheep', 'large', 'more' ] ]

    The original three arrays got converted into a single one with two arrays only. And each of these new arrays represents a single animal with all its into in one place. So, the index 0 tells us that type of animal it is, the index 1 tells us its size, and the index 2 tells us its weight. As a result, the data is now easier to work with. Once you’ve applied whatever operations you need to on the data, you can break it up again using unzip() and send it back to the original source:

    const animalData = _.unzip(groupedAnimals);
    console.log(animalData);
    // [ [ 'duck', 'sheep' ], [ 'small', 'large' ], [ 'less', 'more' ] ]

    The zip/unzip utility isn’t something that will change your life overnight, but it will change your life one day!

    Conclusion 👨‍🏫

    (I put all the source code used in this article here for you to try directly from the browser!)

    The Lodash docs are chock-full of examples and functions that will just blow your mind. In a day and age where masochism seems to be increasing in the JS ecosystem, Lodash is like a breath of fresh air, and I highly recommend that you use this library in your projects!