Filtering an array using a function that returns a promise
Tags: javascript, typescript, promise
Published: 2020-09-03 15:05:24

Things get interesting when you want to filter an array of values, with a function returning promise of boolean instead of just a boolean

### Prerequisite:
- Basic understanding on how the promises work.
- Basic knowledge of Typescript.

It is easy to filter an array of values using a function returning a boolean. Let see an example.


```typescript
const values = [1, 2, 3, 4, 5, 6];
const isEven = (v: number) => v % 2 === 0;
const result = values.filter(isEven);
console.log(result);

// Output
// [ 2, 4, 6 ]
```

In the above code, we use a function called `isEven` to filter an array of numbers and return only the even numbers. We know that, the `isEven` function takes a `number` and returns a `boolean` value representing, whether the number is even or not. 

Lets change the `isEven` function to return `Promise<boolean>` instead of just a `boolean` and try to filter values.

```typescript
const values = [1, 2, 3, 4, 5, 6];
const isEvenPromise = (v: number) => new Promise(res => res(v % 2 === 0));
const result = values.filter(isEvenPromise);

// Output
// [1, 2, 3, 4, 5, 6]
```

As you can see, i got all the values in the output, which is wrong. Now, why did that happen? 

This happened because the filter got a `Promise` as a result of executing the `isEvenPromise` function and not a `boolean`. As per the javascript's truthy concept, an object is always `true`, hence all the values are returned as output.

Now we know what the problem is, but how to solve this? Lets write a function to solve this. 

First, lets define the type of our function to get a clear idea of how the function is going to look like.

```typescript
type Filter = <T>(values: T[], fn: (t: T) => Promise<boolean>) => Promise<T[]>;
```

- First parameter is the array of `values` of type `T` that has to be filtered.
- Second parameter is a function that accepts a value of type `T` as an input and returns a `Promise`, of type `boolean`.
- Return type is a `Promise`, holding an array of type `T`.

One thing to be noted is that, the return type of this function is not `T[]` but `Promise<T[]>`. This is  because, the filter function does not return a `boolean` but returns a `Promise<boolean>`. We cannot remove the value out of a `Promise`. The only we to use the value returned from a `Promise` is either by using a `then` or by using `async` and `await`. 

Now lets write the body of the function.

```typescript
const filterPromise: Filter = async (values, fn) => {
    const promises = values.map(fn);                // Line 1
    const booleans = await Promise.all(promises);   // Line 2
    return values.filter((_, i) => booleans[i]);    // Line 3
};
```
One important thing to note here is that,
>`filter` function in JS passes the `index` of the array as a second argument to the accepted function.

In `Line 1`, we `map` the values array to the `fn` instead of filtering it directly, so that we can obtain the `boolean` values first. In `Line 2`, we convert the array of promises into a promise, holding an array of booleans. We use the `await` keyword here to access the `booleans`. In `Line 3`, we filter the `values` using the `i`th element in the `booleans` array which holds the boolean value of `i`th element.

A representation of what each variable will hold as a result of the execution of each line is shown below. 

For input values `[1, 2, 3, 4, 5, 6]`,

#### Line 1:
```typescript
// As a result of Line 1
const promises = [
    Promise<false>,
    Promise<true>,
    Promise<false>,
    Promise<true>,
    Promise<false>,
    Promise<true>,
]
```

#### Line 2:
```typescript
// As a result of Line 2
const booleans = [
    false,
    true,
    false,
    true,
    false,
    true
]
```

#### Line 3:
```typescript
// Return at Line 3
Promise<[2, 4, 6]>
```

As you can see, the result at `Line 3` is properly filtered even numbers from the input array. 

The entire code is shown below.
```typescript
const values = [1, 2, 3, 4, 5, 6];
const isEvenPromise = (v: number): Promise<boolean> => new Promise(res => res(v % 2 === 0));

type Filter = <T>(values: T[], fn: (t: T) => Promise<boolean>) => Promise<T[]>;
const filterPromise: Filter = async (values, fn) => {
    const promises = values.map(fn);                // Line 1
    const booleans = await Promise.all(promises);   // Line 2
    return values.filter((_, i) => booleans[i]);    // Line 3
};

const result = filterPromise<number>(values, isEvenPromise);

result.then(d => console.log(d));

// Output
// [ 2, 4, 6 ]
```

If you are a fan of one liner like me, then the `filterPromise` function can be written in a single line like below.


```typescript
const filterPromise = (values, fn) => 
    Promise.all(values.map(fn)).then(booleans => values.filter((_, i) => booleans[i]));
```


__Hope you enjoyed! Happy Hacking!__