filterMap - JavaScript filter and map in O(n)
Alex Parra
・2 min read
It's common that we need to get a subset of items from an array. For example, get only the users that are subscribed to the newsletter from the list of all users. This is commonly a job for Array.filter.
/**
* Get list of subscribed users
* @param {User[]} users The list of all users
* @retuns {User[]} Only users that are subscribed
*/
const getSubscribedUsers = users => {
return users.filter(user => user.is_subscribed);
}
It's also common that we need to apply some transformation to a set of data. For example, get a list of full names from a list of users by concatenating each user's first_name
and last_name
.
/**
* Get list of users full names
* @param {User[]} users The list of all users
* @retuns {string[]} Users full names
*/
const getUsersFullNames = users => {
return users.map(user => `${user.first_name} ${user.last_name}`);
}
*But what if we need the full names of the subscribed users only?
Frequently we'll see:
const subscribedUsers = getSubscribedUsers(users);
const subscribedUsersNames = getUsersFullNames(subscribedUsers);
The problem with this approach, which might not be too significant on a small set of users but is when dealing with large sets, is that it requires two loops: the first across every user
and the second across every subscribedUser
.
The method I'm sharing with you here accomplishes the same result but looping just once over the data set – O(n) – thus making it more performant:
const isFn = f => typeof f === 'function';
/**
* Filter and Map an Array in a single loop
* @param {array} arr The list to process
* @param {function} filterFn The filtering logic
* @param {function} mapFn The transforming logic
*/
const filterMap = (arr, filterFn = null, mapFn = null) => {
return arr.reduce((acc, item, i) => {
if (isFn(filterFn) && filterFn(item, i) === false) return acc;
const newItem = isFn(mapFn) ? mapFn(item, i) : item;
return [...acc, newItem];
}, []);
};
And an example usage would be:
const isSubscribed = user => user.is_subscribed;
const getFullName = user => `${user.first_name} ${user.last_name}`;
const subscribedUsersNames = filterMap(users, isSubscribed, getFullName);
In the example above, isSubscribed
is a utility function that will be used to evaluate if the item (each user
) should be kept or excluded, and getFullName
is a utility function that will determine the data we get back in the new list.
Check it out at CodeSandbox with tests:
https://codesandbox.io/embed/js-array-filtermap-mvi1q?fontsize=14&hidenavigation=1&module=%2Findex.ts&previewwindow=tests&theme=dark
Spotted a mistake? Let me know!

Intl Formatting Numbers in Javascript
M. Andrew Darts -

Haye.cr: Expressive string syntax to Array/Hash and Vice-Versa parser for Crystal. Based on the javascript version.
Wilson Tovar -

Triangle of asterisks
Cesare Ferrari -

If you can put the
arr
argument last then it would be easier to do partial application.This is essentially what transducers are used for. When you have a large data set and want to process it in a single pass with small utility functions. Here's an example codesandbox:
codesandbox.io/embed/peaceful-maye...
And here's an article about them that explains them better than I ever could:
medium.com/javascript-scene/transd...