Eight (Groups of) Lodash Functions I Reach for the Most

pearlzsquare

Pearl Zeng-Yoders

Posted on March 1, 2022

Eight (Groups of) Lodash Functions I Reach for the Most

Wanted to share some Lodash functions that I reach out to a lot either in our React frontend or Node backend. I find them handy and that they improve code readability.

1. lodash/get

Use case: Get a property or nested property from an object when the object or its properties are potentially undefined. Can also provide a default value if needed.

// Examples: get company and department properties of a person,
// variable `person` has the following type interface:
interface Person {
    firstName: string;
    lastName: string;
    company?: {
        name: string;
        // ...other company props
        department?: {
            name: string;
            // ...other department props
        }
    }
}

// Example 1: Get the name of the person's company, 
// if undefined, give it ‘Company S’;
// Using syntax 1: use string to express props to look up
const companyName = get(person, 'company.name', 'Company S');

// Example 2: Get the name of the person's department, 
// if undefined, give it ‘Department E’;
// Using syntax 2: use array of strings to express props to look up
cont departmentName = get(person, ['company', 'department', 'name'], 'Department E');
Enter fullscreen mode Exit fullscreen mode

Both syntax allow the use of variables if the look up property name is a variable:

// syntax 1:
const somePropOfCompany = get(person, `company[${propName}]`);

// syntax 2: 
const somePropOfCompany = get(person, ['company', propName]);
Enter fullscreen mode Exit fullscreen mode

2. lodash/partition, lodash/difference, & lodash/intersection

Use case: I reach for these functions when needing to handle parts of an array differently.

lodash/partition

// Example: Given an array of items, handle featured items and the rest of the items differently,
// variable `item` has the following type interface:
interface Item {
    name: string;
    isFeatured: boolean;
    category: 'Hobby' | 'Skill' | 'Other';
};

const itemList: Item[] = [
    {
        name: 'my hobby 1',
        isFeatured: false,
        category: 'Hobby',
    },
    {
        name: 'my hobby 2',
        isFeatured: true,
        category: 'Hobby',
    },
    {
        name: 'my skill 1',
        isFeatured: true,
        category: 'Skill',
    },
    {
        name: 'my item 100',
        isFeatured: false,
        category: 'Other',
    }
  // ... more items like the above
];

// Use partition to get a list of featured items and a list of the rest of the items
const [featuredItems, restOfItems] = partition(itemList, { isFeatured: true });

// The second argument can also be a function instead of a property object,
// for example, partition a list of featured hobby items from the rest of the items
const [featuredHobbyItems, restOfItems] = partition(itemList, 
    item => item.isFeatured && item.category === 'Hobby');
Enter fullscreen mode Exit fullscreen mode

lodash/difference

// Example: Given a list of incoming members and a list of members from user input,
// get the members to add
const currentMembers = [
  // ...an array of existing members from the API
]; 
const incomingMembers = [
    // ...an array of new members from the form input
]; 
const membersToAdd = difference(incomingMembers, currentMembers);
Enter fullscreen mode Exit fullscreen mode

lodash/intersection

// Example: given the same member list from the above, get the members to remove
const membersToRemove = intersection(currentMembers, incomingMembers);
Enter fullscreen mode Exit fullscreen mode

3. lodash/keyBy & lodash/groupBy

Use case: Build a lookup – keyBy uses one object property as the key, and groupBy groups array items with the same keys into an array under that key.

lodash/keyBy

// Example: Given a list of people with the following type interface,
// get a lookup of people by id
interface Person {
    id: string;
    firstName: string;
    lastName: string;
    companyName: string;
};

const people: person[] = [
    // ...list of people
];

const peopleLookup = keyBy(people, 'id');

// Resulting interface:
interface PeopleLookup {
    [id: string]: Person;
};
Enter fullscreen mode Exit fullscreen mode

lodash/groupBy

// Example: Given a list of people like the above,
// get a lookup of people by company name
const peopleByCompany = groupBy(people, 'companyName');

// Resulting interface:
interface PeopleByCompany {
    [companyName: string]: Person[];
};
Enter fullscreen mode Exit fullscreen mode

4. lodash/compact & lodash/isEmpty

Use case: Make sure intended operations don’t happen on falsey items.

lodash/compact

// Example: Given a list of people with the following interface,
// get a list of profile photo urls
interface Person {
    firstName: string;
    lastName: string;
    profilePictureUrl?: string;
};

const people: Person[] = [
    // ...list of people
];

const profilePictureUrls = compact(people.map(p => p.profilePictureUrl));
// Using compact here will get rid of any undefined, null, or empty values
Enter fullscreen mode Exit fullscreen mode

lodash/isEmpty

// Example 1: Given the people list like the above,
// call the API to upload the photos if profilePictureUrls is not empty
if (!isEmpty(profilePictureUrls)) {
    // call the API
}; // Can also be accomplished by checking array length

// Example 2: Given an object of props to update for a person,
// call the person update endpoint if there are items to update
interface PersonUpdate {
    email?: string;
  phoneNumber?: string;
    company?: {
        name?: string;
        department?: string;
    };
  // ...many more properties
}

if (!isEmpty(PersonUpdate)) {
    // call the person update endpoint
}
Enter fullscreen mode Exit fullscreen mode

5. lodash/pick, lodash/omit, lodash/uniq, & lodash/isEqual

Use case: Grab intended properties to send to the API or for display on the UI.

// Examples: Given updatePersonRequest,
const validPersonProps = ['firstName', 'lastName', 'email', 'number'];
// Use pick to grab valid props to send to the updatePerson API endpoint
await updatePerson(pick(updatePersonRequest, validPersonProps));

const propsToSendSeparately = ['profilePhotoUrl', 'coverPhotoUrl'];
// Use omit to omit properties that are handled via a different endpoint
await updatePerson(omit(updatePersonRequest, propsToSendSeparately));

// Use isEqual to decide whether to call the updateEndpoint,
// it performs a deep comparison
if (!isEqual(currentPersonValue, editedPersonValue)) {
    // call the update endpoint
};

// Let's say for every companyName, we need to create a company profile page
const personList = [
    {
        firstName: 'John'
        lastName: 'Doe',
        companyName: 'Company A'
  },
    {
        firstName: 'Sara'
        lastName: 'Smith',
        companyName: 'Company A'
  },
    {
        firstName: 'James'
        lastName: 'Bond',
        companyName: 'Company B'
  },
  // ...more person objects like the above
];
// Use uniq to avoid duplicated company page creation
const namesForCompanyPageCreation = uniq(personList.map(p => p.companyName));
Enter fullscreen mode Exit fullscreen mode

6. lodash/sortBy & lodash/orderBy

Use case: Sort and order array items.

sortBy sorts items in ascending order, and orderBy allows specifying the sort order.

// Examples:
// Use sortBy to sort timezones in UTC offset, 
// and if offset is the same, sort by displayName
const timezonesOrderedByUtcOffset = sortBy(timezones, [
    tzItem => tz.utcOffect,
    'displayName'
]); // This array accepts both a callback and a property name

// Get the latest blog post by first using orderBy to order posts in descending order
const [latestBlogPost] = orderBy(blogposts, 'createdAt', 'desc');
// The second and third arguments can both be arrays as well, e.g.
// Order the blog posts by descending order for creation date, 
// and ascending order for edited date
const orderedPosts = orderBy(blogposts, ['createdAt', 'editedAt'], ['desc', 'asc']);
Enter fullscreen mode Exit fullscreen mode

7. lodash/chunk

Use case: Chunk items when inserting into the sql database to avoid inserting a big amount of data at once.

// Example: Insert over 10,000 people into the person table.
// Chunk the data into 500 chunks first and then insert with a custom insertMany function
const chunkedPeopleData = chunk(peopleData, 500);
await Promise.all(chunkedPeopleData.map(data => 
    insertMany('person', data)
));
Enter fullscreen mode Exit fullscreen mode

8. lodash/sumBy

Decided to use sumBy to wrap up the post ;)

Use case: Get the sum.

// Example: Get total post reads
interface Post {
    name: string;
    viewCount: number;
}

const posts: Post[] = [
    // ...a list of posts
];

const totalPostReads = sumBy(posts, 'viewCount');
Enter fullscreen mode Exit fullscreen mode

That’s It

Those are the Lodash functions I normally reach for, along with their examples. There are debates on whether to use Lodash, given the weight it adds to the application. Additionally, newer versions of Javascript can accomplish a lot more functions that Lodash was trying to simplify. One thing came to mind was flat() that was introduced with ES2019. It handles deep flatten nicely:

const arr = [0, 1, 2, [[[3, 4]]], [[[[5, 6]], [7, 8]]]];
// To turn the above array into [1, 2, 3, 4, 5, 6, 7, 8]:
// Flat deep with ES2019
const flatArr = arr.flat(Infinity);
// Flat deep with lodash
const loFlatArr = flattenDeep(arr);
Enter fullscreen mode Exit fullscreen mode

And many of the above use cases can be achieved by Javascript native function reduce, but I find the Lodash functions provide more communicative APIs. And to reduce (😉) the impact of library weight, I would only import the needed modules:

// Instead of this:
import { isEmpty } from 'lodash';

// Do this:
import isEmpty from 'lodash/isEmpty';
Enter fullscreen mode Exit fullscreen mode

Let me know your thoughts in the comments. Happy Lodashing!

💖 💪 🙅 🚩
pearlzsquare
Pearl Zeng-Yoders

Posted on March 1, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related