Code Review Videos > Map, Filter, Reduce > [Easy] Filter in Typescript

[Easy] Filter in Typescript

The following are the TypeScript solutions to the filter problems defined under Map, Filter, Reduce.

As TypeScript / JavaScript are languages I am comfortable with, the only things I will call out are those that I think are of any interest. Always happy to answer questions though. Just leave a comment.

Easy Filter

// easy-filter.spec.ts

import { filterBool, filterNumbers, filterString } from "./easy-filter";

describe("easy filter", () => {
  describe("filterNumbers", () => {
    test("zero element array", () => {
      const given: number[] = [];
      expect(filterNumbers(given)).toEqual([]);
    });

    test("one element array - odd", () => {
      const given = [1];
      expect(filterNumbers(given)).toEqual([]);
    });

    test("one element array - even", () => {
      const given = [2];
      expect(filterNumbers(given)).toEqual([2]);
    });

    test("multiple element array", () => {
      const given = [9, 8, 7, 6, 5, 4, 3, 2, 1];
      expect(filterNumbers(given)).toEqual([8, 6, 4, 2]);
    });
  });

  describe("filterString", () => {
    test("zero element array", () => {
      const given: string[] = [];
      expect(filterString(given)).toEqual([]);
    });

    test("one element array - empty", () => {
      const given = [""];
      expect(filterString(given)).toEqual([]);
    });

    test("one element array - valid", () => {
      const given = ["b"];
      expect(filterString(given)).toEqual(["b"]);
    });

    test("one element array - invalid", () => {
      const given = ["a"];
      expect(filterString(given)).toEqual([]);
    });

    test("multiple element array", () => {
      const given = ["aaa", "bbb", "ccc", "a", "AAA", "Abba", ""];
      expect(filterString(given)).toEqual(["bbb", "ccc"]);
    });
  });

  describe("filterBool", () => {
    test("zero element array", () => {
      const given: boolean[] = [];
      expect(filterBool(given)).toEqual([]);
    });

    test("one element array - truthy", () => {
      const given = [true];
      expect(filterBool(given)).toEqual([true]);
    });

    test("one element array - falsy", () => {
      const given = [false];
      expect(filterBool(given)).toEqual([]);
    });

    test("multiple element array - truthy", () => {
      const given = [true, true];
      expect(filterBool(given)).toEqual([true, true]);
    });

    test("multiple element array - falsy", () => {
      const given = [false, false, false];
      expect(filterBool(given)).toEqual([]);
    });

    test("multiple element array - mixed", () => {
      const given = [false, true, false, true, false, true];
      expect(filterBool(given)).toEqual([true, true, true]);
    });
  });
});Code language: PHP (php)

And the associated implementation:

// easy-filter.ts

export const filterNumbers = (input: number[]): number[] =>
  input.filter((number) => number % 2 === 0);

export const filterString = (input: string[]): string[] =>
  input.filter(
    (string) => string.trim().length && !string.trim().toLowerCase().includes("a")
  );

export const filterBool = (input: boolean[]): boolean[] =>
  input.filter((bool) => bool);

export default {
  filterBool,
  filterNumbers,
  filterString,
};Code language: JavaScript (javascript)

There are some things we have already covered in the reduce implementation which repeat here:

There are also some new things to cover.

The Remainder Operator (%)

The first challenge is to determine if a given number is even or odd.

A very common solution to this problem is to use the remainder operator – % – which I’ll be honest I have always called that the modulo operator, or just simply modulo.

But it turns out modulo and remainder are two different things, and I guess I should count myself lucky that this has never bitten me on the ass.

Anyway, the gist of this is can we divide the number by 2 and end up without a remainder:

number % 2 === 0

// or

(2 % 2 = 0) === 0 // true

// or 

(3 % 2 = 1) === 0 // false

// or 

(1 % 2 = 1) === 0 // false

// or

(4 % 2 = 0) === 0 // trueCode language: JavaScript (javascript)

This should hold true for 0 and negative numbers, e.g. -100 % 2 === 0 === true

Have a play around with it yourself at JSBin.

To directly address the problem then:

input.filter((number) => number % 2 === 0);

//

[2,4,6,7].filter((number) => number % 2 === 0);Code language: JavaScript (javascript)

There is an implicit truthy check happening here. If the code is expanded it becomes a little clearer:

 [2,4,6,7].filter((number) => true === (number % 2 === 0));Code language: JavaScript (javascript)

The filter function is going to keep anything where the outcome of the function is true, and it will discard (or filter out) anything that returns false.

Short Circuit

I’m talking about this:

export const filterString = (input: string[]): string[] =>
  input.filter(
    (string) => string.length && !string.trim().toLowerCase().includes("a")
  );Code language: JavaScript (javascript)

But if you’re old enough to remember the 1980s then I suspect you’re thinking about this:

Right, so we’ve already touched on boolean logic.

A short circuit is when we chain together two or more conditions that can resolve to true or false.

As soon as the first false is hit, no further checks are made.

Essentially if we have:

true && true && true && falseCode language: JavaScript (javascript)

Then the computer would do four checks.

However, if we had:

true && false && true && true && trueCode language: JavaScript (javascript)

Then it would never check beyond the first false.

That can be pretty useful if you have some computationally expensive processes and you have some much quicker checks, and there is no point doing the expensive stuff if the cheap stuff has already failed.

To cover our very specific example:

const string1 = "cat";
const string2 = "";

const ourStringChecker = 
  (s: string) => 
    // the given string has a length greater than 0 after all leading and trailing whitespace is removed
    s.trim().length 
    // and
    && 
    // the lower cased version of the string does not (the leading !) include the letter "a"
    !s.trim().toLowerCase().includes("a");


console.log(ourStringChecker(string1)); // true

"cat".length // 3, which is a truthy value
// so we continue
 && 
// "cat" would include "a", so that's true - and so false === true would return false
false === "cat".trim().toLowerCase().includes("a")


console.log(ourStringChecker(string2)); // false

"".length // 0, which is a falsy value
// so the process would immediately return here with falseCode language: JavaScript (javascript)

Huh? input.filter((bool) => bool);

This kinda puts everything together.

We know filter will keep anything true and remove anything false.

We’re working directly with boolean values, so we can simply return the given value. It’s either going to be true or false and so the outcome is super easy, even if the function looks kinda pointless.

Incidentally I don’t think there’s a better built in way to check for truthiness in this instance.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.