Welcome aboard, TypeScript enthusiasts! Today, we’re diving into the world of array filtering with TypeScript. Filtering arrays is a crucial skill every developer should have in their toolkit, allowing us to effortlessly sift through data, and extract exactly what we need. So, grab your favourite beverage, sit back, and let’s embark on a journey to master the art of filtering arrays in TypeScript! πŸš€

Arrays in TypeScript

If you’re reading this article, you’re probably already familiar with arrays in TypeScript, but just for a quick refresh, here’s what they look like:

const array: type[] = [item1,  item2, item3, ...];

We define an array type with a type, followed by the square array brackets.

What is filtering?

Filtering an array put simply, means reducing an array to only the items that meet a certain condition. We do this through the .filter() method on arrays:

const filtered = arr.filter((item, index, array) => {
    // if true item is pushed to results and the iteration continues
    // returns empty array if nothing found
});

The array takes a callback function as a parameter, which gets called for every item in the array. Most of the time you’ll probably end up just using the item parameter, but the index and array parameters are also available.

Example

Let’s start off with a simple example. We’ll start off with this array:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];

I want to get all the numbers in this array greater than 4. This is pretty straightforward, first, we write our filter function:

const gt4 = (n: number) => n > 4;

(We don’t need to write this separately, but it can help to separate your logic from the filter method)

const greaterThan4 = numbers.filter(gt4);
console.log(greaterThan4);
//[ 5, 6, 7, 8 ]

Then we pass our function as a parameter to the filter array. Filter doesn’t modify the original array, it returns a new array with the filtered contents.

And since our function is so small, we might not need to bother defining it separately:

const greaterThan4 = numbers.filter((n) => n > 4);

Objects

So far we’ve only been dealing with primitives, but filter also works on objects, 2D arrays – anything you can put in an array. Let’s take a look at an example that uses objects. Let’s make a social network, a very simple one, all we can do is add friends. Here are our users:

type User = { name: string; friends: string[] };
const adam: User = { name: 'adam', friends: ['brian', 'carl'] };
const brian: User = { name: 'brian', friends: ['adam'] };
const carl: User = { name: 'carl', friends: ['adam'] };

const allUsers = [adam, brian, carl];

I want to be able to filter through the “allUsers” array and find all the friends for a specific user.

We’ll start off with being able to check if a user is friends with another user:

function friends(user1: User, user2: User) {
    return (
        user1.friends.includes(user2.name) && user2.friends.includes(user1.name)
    );
}

Pretty simple, just check if they’re in each others’ friends lists.

Since we’ve started off with this logic, all we need to do is call this function from our filter method:

const adamsFriends = allUsers.filter((u) => friends(adam, u));
console.log(adamsFriends);
[
  { name: 'brian', friends: [ 'adam' ] },
  { name: 'carl', friends: [ 'adam' ] }
]

Simple and straightforward!

Duplicates

We can also use the map array to filter out duplicates in an array. Let’s take a look at this:

const numbers = [1, 1, 2, 3, 3, 4, 5, 5, 6];
const unique = numbers.filter((n, i, arr) => i === arr.indexOf(n));

We have an array with some duplicate numbers. In order to filter out the duplicates, we can use all 3 parameters provided to us. The logic here relies on the indexOf array, which will find the index of the first instance of our value.

So for the first ‘1’, i is 0, and so is the index that indexOf will return. For the second 1, i is 1, but indexOf will find the first instance first, and since 0 != 1, our filter returns false.

If you have the references, this code will work with objects. If we go back to our previous example:

type User = { name: string; friends: string[] };

const adam: User = { name: 'adam', friends: ['brian', 'carl'] };
const brian: User = { name: 'brian', friends: ['adam'] };
const carl: User = { name: 'carl', friends: ['adam'] };

const allUsers = [adam, brian, carl];

We might have some method that gets everyone in a friends list, in which case our array might look like this:

const hasFriends = [brian, carl, adam, adam];

Depending on how you do it, these might still be the original references, so we can still filter our array the same way:

const hasFriends = [brian, carl, adam, adam].filter(
    (n, i, arr) => i === arr.indexOf(n)
);

But let’s rewrite our array. If the result came from something like an API call, you’re pretty likely to get something like this back:

const hasFriends = [
    { name: 'brian', friends: ['adam'] },
    { name: 'carl', friends: ['adam'] },
    { name: 'adam', friends: ['brian', 'carl'] },
    { name: 'adam', friends: ['brian', 'carl'] },
];

Same array, but instead of using the original references, we now have a list of values. Suddenly our original method doesn’t work, the objects will get compared by reference, and the references aren’t the same. How do we fix this?

You could map the objects to and from strings:

const hasFriends = [
    { name: 'brian', friends: ['adam'] },
    { name: 'carl', friends: ['adam'] },
    { name: 'adam', friends: ['brian', 'carl'] },
    { name: 'adam', friends: ['brian', 'carl'] },
]
    .map((u) => JSON.stringify(u))
    .filter((n, i, arr) => i === arr.indexOf(n))
    .map((u) => JSON.parse(u));

Or alternatively, if you have some kind of unique id, compare them through those. In our example, everyone has a unique name:

const hasFriends = [
    { name: 'brian', friends: ['adam'] },
    { name: 'carl', friends: ['adam'] },
    { name: 'adam', friends: ['brian', 'carl'] },
    { name: 'adam', friends: ['brian', 'carl'] },
].filter((u, i, arr) => i === arr.findIndex((u2) => u.name === u2.name));

This is likely to be faster than any deep equality comparison, since we’re just comparing one field.

Conclusion

Thanks for reading! Filter is a simple but very versatile function, so I hope with these few examples I was able to showcase what the method is able to do, and how to use it. If you liked this article, or if you’re having any troubles, feel free to leave a comment below!

Avatar photo
πŸ‘‹ Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

πŸ’¬ Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.