back to series

Implement a deepFilterArray function

Deep Filtering is not a standard term defined in JavaScript, but it is commonly well understood as the process of perform filtering operation on each depth of a nested multi-dimensional array.

Variation: Basic implementation

1. Basic implementation

We have to implement a deepFilterArray() utility function which takes a nested multi-dimensional array and a filter callback and returns a new array with element filtered. The depth of elements on array must be maintained.

./src/deepFilterArray.js
function deepFilterArray(arr, filterFn) {
const result = [];
if (!arr.length) {
return result;
}
arr.forEach((item) => {
if (Array.isArray(item)) {
// recursively call the filter method when it's a nested array
result.push(deepFilterArray(item, filterFn));
} else {
const isTruthy = filterFn(item);
if (isTruthy) {
result.push(item);
}
}
});
return result;
}
// Usage
const input = [1, [2, [3, [[4], { a: 1, b: 4 }], 4, 'hi'], ['hello', [['welcome', 10]], 11]]];
const output = deepFilterArray(input, function (e) {
return typeof e === 'number' && e > 2;
});
/**
* Yields: [[[3,[[4]],4],[[[10]],11]]]
*/
console.log(JSON.stringify(output));

Variation: Custom this binding

2. Custom this binding implementation

In the above implementation, we simply invoked the filter callback, without binding the this keyword to any value. For this implementation, we need to bind the this value to the original input array.

Most of the implementation will remain the same, so I am highlighting only the new changes made to support the same.

./src/deepFilterArray.js
function deepFilterArray(arr, filterFn, ogArg = arr) {
const result = [];
if (!arr.length) {
return result;
}
arr.forEach((item) => {
if (Array.isArray(item)) {
result.push(deepFilterArray(item, filterFn, ogArg));
} else {
const isTruthy = filterFn.call(ogArg, item);
if (isTruthy) {
result.push(item);
}
}
});
return result;
}
// Usage
const input = [1, [2, [3, [[4], { a: 1, b: 4 }], 4, 'hi'], ['hello', [['welcome', 10]], 11]]];
const output = deepFilterArray(input, function (e) {
// this refers to the original array, so this.length=2
return typeof e === 'number' && e > this.length;
});
/**
* Yields: [[[3,[[4]],4],[[[10]],11]]]
*/
console.log(JSON.stringify(output));
Variation: Counting filtered elements

3. Counting filtered elements implementation

In both the variations above, we returned the new filtered array back. However, it might be asked to simply count and return the number of items which yields a turthy value for the callback function.

The implementation for this version remains almost the same, so I will highlight only the new changes made.

./src/deepFilterArray.js
function deepFilterArray(arr, filterFn, ogArg = arr) {
let count = 0;
if (!arr.length) {
return count;
}
arr.forEach((item) => {
if (Array.isArray(item)) {
count += deepFilterArray(item, filterFn, ogArg);
} else {
const isTruthy = filterFn.call(ogArg, item);
if (isTruthy) {
count++;
}
}
});
return count;
}
// Usage
const input = [1, [2, [3, [[4], { a: 1, b: 4 }], 4, 'hi'], ['hello', [['welcome', 10]], 11]]];
const output = deepFilterArray(input, function (e) {
return typeof e === 'number' && e > this.length;
});
// yields 5
console.log(output);

Further Reading

I strongly encourage you to explore and tackle additional questions in my Recursion Questions for Frontend Interviews blog series.

By doing so, you can enhance your understanding and proficiency with recursion, preparing you to excel in your upcoming frontend interviews.

Wishing you best. Happy Interviewing 🫡.