An array often needs to be filtered by a condition. That can easily be done filter
function that is implemented in Array object.
Let’s see how to filter an array in JavaScript/TypeScript.
Syntax
The syntax is simple. filter
function requires a callback. The callback must return boolean.
array.filter(callback);
The callback can directly be defined in it by using an arrow function as known as a lambda function. If the logic can be written in a single line, return
keyword can be excluded.
array.filter((element) => true);
If multiple lines are needed, write the code in the brackets.
array.filter((element) => {
// do something with element here
// return true or false at the end
return true;
});
Check the following post if you are not familiar with an arrow function.
Filter number array
Let’s check the actual usage. In this example, it filters number that is bigger than 10.
const numArray = [5, 1, 78, 2, 42, 4, 11, 4];
console.log(numArray.filter((x) => x > 10)); // [ 78, 42, 11 ]
As you can see above, numbers are filtered as expected.
If we need to filter discontinuous numbers, we can define them in the following way.
console.log(numArray.filter((x) => x === 78 || x === 42 || x === 11)); // [ 78, 42, 11 ]
console.log(numArray.filter((x) => [78, 42, 11].includes(x))); // [ 78, 42, 11 ]
Both of them result in the same. The first one is simpler but it can be much longer if there are lots of elements needed. The second way is shorter and easy to read. The advantage of the second way is that the filtering condition/list can be defined separately. It makes the code more readable.
If we want to know only whether the array contains those values or not, use some
function instead. It returns a boolean instead of the array.
console.log(numArray.some((x) => [78, 42, 11].includes(x))) // true
Filter string array
How can we filter string array? It’s basically the same. Just compare the value with the element.
const strArray = ["aa", "bb", "aabb", "aa", "cc", "aacc"];
console.log(strArray.filter((x) => x == "aa")); // [ 'aa', 'aa' ]
If we need strings that contain something, use includes
method or a regex.
console.log(strArray.filter((x) => x.includes("aa"))); // [ 'aa', 'aabb', 'aa', 'aacc' ]
console.log(strArray.filter((x) => /aa.*/.test(x))); // [ 'aa', 'aabb', 'aa', 'aacc' ]
Filter array of objects by property value
We learned how to filter number/string array but how can we filter for object array? Okay, let’s dig into it further. Assume that we have the following class.
class Product {
constructor(
public id: number,
public name: string,
) { }
}
const objArray: Product[] = [
new Product(100, "water"),
new Product(101, "apple juice"),
new Product(102, "banana juice"),
new Product(200, "banana"),
new Product(201, "apple"),
];
Once we know the data type in the callback, we can easily access the desired property.
console.log(objArray.filter((item) => item.id >= 100 && item.id < 200));
// [
// Product { id: 100, name: 'water' },
// Product { id: 101, name: 'apple juice' },
// Product { id: 102, name: 'banana juice' }
// ]
console.log(objArray.filter((item) => item.name.includes("banana")));
// [
// Product { id: 102, name: 'banana juice' },
// Product { id: 200, name: 'banana' }
// ]
That’s easy.
Filter mixed data type array
In some cases, there are multiple types in an array. Imagine that we define an interface or base class that some other classes implement/extend it. In this case, an array has mixed data types in it.
I defined the following two classes and use them in an array.
class PcProduct extends Product {
constructor(
id: number,
name: string,
public ram: string,
public cpu: string,
) {
super(id, name);
}
}
class AlcoholProduct extends Product {
constructor(
id: number,
name: string,
public type: string,
) {
super(id, name);
}
}
const mixedArray = [
new PcProduct(100, "laptop-pc-A", "8GB", "2.4GHz"),
new PcProduct(101, "laptop-pc-B", "16GB", "2.4GHz"),
new PcProduct(102, "desktop-pc-A", "32GB", "3.0GHz"),
new AlcoholProduct(301, "Weis Beer", "Beer"),
new AlcoholProduct(302, "Bombay Sapphire", "Gin"),
new AlcoholProduct(303, "Glenfarclas", "Whiskey"),
new AlcoholProduct(304, "Tanqueray", "Gin"),
];
Let’s extract alcohol products from the array. We can simply filter them by comparing id > 300
but we will implement it in a different way.
We can simply compare the data type by using instanceof
. Another way is to check whether the object has the property name that is used in one of the classes.
console.log(mixedArray.filter((x) => x instanceof AlcoholProduct))
// [
// AlcoholProduct { id: 301, name: 'Weis Beer', type: 'Beer' },
// AlcoholProduct { id: 302, name: 'Bombay Sapphire', type: 'Gin' },
// AlcoholProduct { id: 303, name: 'Glenfarclas', type: 'Whiskey' }
// ]
console.log(mixedArray.filter((x) => x.hasOwnProperty("type")))
// [
// AlcoholProduct { id: 301, name: 'Weis Beer', type: 'Beer' },
// AlcoholProduct { id: 302, name: 'Bombay Sapphire', type: 'Gin' },
// AlcoholProduct { id: 303, name: 'Glenfarclas', type: 'Whiskey' }
// ]
In our case, type
is defined only in AlcoholProduct
class. If another class has the same property, this way can’t be applied. Note that the IntelliSense doesn’t recognize the data type if we use hasOwnProperty
.
If it’s not a class but interface, we can’t use instanceof
keyword. In this case, we should define User-Defined Type Guards as explained in the following post.
We know how to filter the target class now. Next, let’s filter Gin product from it.
We will compare the class first to let IntelliSense know the data type. Then, it can show type
property. Return false if the product is not AlcoholProduct
.
console.log(mixedArray.filter((x) => {
if ((x instanceof AlcoholProduct)) {
return x.type === "Gin";
}
return false;
}));
// [
// AlcoholProduct { id: 302, name: 'Bombay Sapphire', type: 'Gin' },
// AlcoholProduct { id: 304, name: 'Tanqueray', type: 'Gin' }
// ]
This can be written in one-liner.
console.log(mixedArray.filter((x) => x instanceof AlcoholProduct && x.type === "Gin"));
The result is of course the same as the previous one.
Comments