Eight (Groups of) Lodash Functions I Reach for the Most
Tags: javascript, refactorit, typescript, datamanipulation
Published: 2022-03-01 01:38:39

Wanted to share some [Lodash functions](https://lodash.com/docs/4.17.15) 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.

```tsx
// 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');
```

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

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

// syntax 2: 
const somePropOfCompany = get(person, ['company', propName]);
```

## 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`

```tsx
// 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');
```

### `lodash/difference`

```tsx
// 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);
```

### `lodash/intersection`

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

## 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`

```tsx
// 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;
};
```

### `lodash/groupBy`

```tsx
// 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[];
};
```

## 4. `lodash/compact` & `lodash/isEmpty`

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

### `lodash/compact`

```tsx
// 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
```

### `lodash/isEmpty`

```tsx
// 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
}
```

## 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.

```tsx
// 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));
```

## 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.

```tsx
// 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']);
```

## 7. `lodash/chunk`

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

```tsx
// 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)
));
```

## 8. `lodash/sumBy`

Decided to use `sumBy` to wrap up the post ;)

Use case: Get the sum. 

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

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

const totalPostReads = sumBy(posts, 'viewCount');
```

## 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:

```tsx
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);
```

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:

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

// Do this:
import isEmpty from 'lodash/isEmpty';
```

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