How to filter an array of numbers using different intervals?

In the example code below I want to filter numbersArray based on different intervals. An interval is defined by a combination of 2 arrays with the lower and upper bound of said interval. How do I identify matches or non-matches like below?

const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];

If the code works the following test should return true:

matches == [2, 5, 9];
nonmatches == [1, 3, 4, 6, 7, 8, 10];
  

Assume the arrays contains more than 1000 items and that the numbers doesn’t follow any pattern.

Below is a less readable but more realistic scenario.

let numbersArray = [];
let lowerBound = [];
let higherBound = [];

for (let i = 0; i< 1000; i++){
  numbersArray.push(i);
}

for(let i = 0; i < 100; i++) {
  lowerBound.push(i * 10);
  higherBound.push((i * 10) + Math.random() * 10);
}

Answer

I’m going to assume that we can change the data structure a little bit because this is quite awkward to work with:

const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];

If the elements at the same indexes are meant to be together then let’s just do that:

const bound = [[1, 3], [4, 6], [8, 10]];

Will come to bound later.

Now let’s build a curried function that validates n if a < n && n < b:

const between = (a, b) => n => a < n && n < b;

const x = between(1, 3);
const y = between(4, 6);

x(1); // false
x(2); // true
y(1); // false
y(5); // true

Now let’s build another curried function that validates n if at least one function returns true:

const or = (...fns) => n => fns.some(fn => fn(n));

const check = or(x, y);

check(1); // false
check(2); // true
check(5); // true

We will now transform bound into an or function after we transformed each pair into a between function:

const bound = [[1, 3], [4, 6], [8, 10]];
const check = or(...bound.map(([a, b]) => between(a, b)));

check is now a function that takes an n and returns true if n is between 1 and 3 or between 4 and 6, …

const between = (a, b) => n => a < n && n < b;
const or = (...fns) => n => fns.some(fn => fn(n));

const bound = [[1, 3], [4, 6], [8, 10]];
const check = or(...bound.map(([a, b]) => between(a, b)));

const [nomatch, match] =
  [1,2,3,4,5,6,7,8,9,10].reduce(
    (acc, n) =>
      (acc[+check(n)].push(n), acc),
        [[], []]);

console.log(`match: [${match}]`);
console.log(`no match: [${nomatch}]`);

Leave a Reply

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