-1

I have ranges described as string

let ranges = "0,71-140,34,142-216,20-30,7"

(not sorted; one number eg 34 means range 34-34).

  • How to check that number num is in some range (of given ranges)
  • How to check that number is smaller than smallest range or bigger than biggest range?

This is inversion of this question.

const isInRanges = (ranges, num) => {
  return false; // magic here
}

const isOutOfRanges = (ranges, num) => {
  return false; // magic here
}

// ------------------------------------------------
// TESTS - we should always get TRUE in console
// ------------------------------------------------

let myRanges = "0,71-140,34,142-216,20-30,7";

// is in tests
let casesIn = [
  [0, true],
  [25, true],
  [35, false],
  [200, true],
  [8, false]
];

for (const c of casesIn) {
  console.log(c[0], isInRanges(myRanges, c[0]) == c[1])
}

// is out tests
let casesOut = [
  [-2, true],
  [60, false],
  [300, true],
  [7, false]
];

for (const c of casesOut) {
  console.log(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}

Solution will be two functions (look on snippet) which returns true/false - and pass all test-cases (we should always see 'true' on the console).

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • 1
    @MrSmith42 - Finally I not get stuck - I give [answer](https://stackoverflow.com/a/75073362/860099) to my own question - which is [allowed by SO](https://ibb.co/MsNFY7N) - I spend some time to find solution of this problem, so i publish it for future reades (to save they time) – Kamil Kiełczewski Jan 10 '23 at 17:31
  • My own response would be the following: https://jsfiddle.net/davidThomas/tyq63eL7/, but I can't work out what your required output/result is – David Thomas Jan 10 '23 at 17:36
  • @DavidThomas thank for your comment - I update question - does it explain something more to you? – Kamil Kiełczewski Jan 10 '23 at 17:38
  • My demo does return a Boolean, so I guess it could be considered an answer? – David Thomas Jan 10 '23 at 17:40
  • @DavidThomas if you provide in it, two functions (which pass tests in question) - I think it will be ok – Kamil Kiełczewski Jan 10 '23 at 17:41
  • Can you explain clearly what the input to the function should be? And exactly what output you expect from each input? It's entirely possible that I'm missing something obvious, though. – David Thomas Jan 10 '23 at 17:43
  • @DavidThomas So for both functions: the input is string with ranges (const myRanges in snippet) and number (num in snippet) which we want to check. The output is `true` or `false`. – Kamil Kiełczewski Jan 10 '23 at 17:50

2 Answers2

1

Here is my answer (for future generations) - but may be someone have better?

const isInRanges = (ranges, num) => {
  return ranges.split(',')
    .map(r => r.split('-'))
    .some(r => r.length == 1 ? num == +r[0] : num >= +r[0] && num <= +r[1]);
}

const isOutOfRanges = (ranges, num) => {
  const sorted = ranges.match(/\d+/g).map(Number).sort((a, b) => a - b);
  return num < sorted.at(0) || num > sorted.at(-1);
}

// ------------------------------------------------
// TESTS - we should always get TRUE in console
// ------------------------------------------------

let myRanges = "0,71-140,34,142-216,20-30,7";

// is in tests
let casesIn = [
  [0, true],
  [25, true],
  [35, false],
  [200, true],
  [8, false]
];

for (const c of casesIn) {
  console.log(c[0], isInRanges(myRanges, c[0]) == c[1])
}

// is out tests
let casesOut = [
  [-2, true],
  [60, false],
  [300, true],
  [7, false]
];

for (const c of casesOut) {
  console.log(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
1

The code originally posted by @KamilKiełczewski can be trimmed down a bit, so that it looks like this.

const isInRanges = (ranges, num) => {
  return ranges.split(',')
    .map(r => r.split('-')) // we're splitting right away
    .some(r => r.length == 1 ? num == +r[0] : num >= +r[0] && num <= +r[1]);
}

const isOutOfRanges = (ranges, num) => {
  // we're avoiding the sorting ...
  const sorted = ranges.match(/\d+/g).map(Number);
  // ... because we're going to use min and max
  return num < Math.min(...sorted) || num > Math.max(...sorted);
}

// ------------------------------------------------
// TESTS - we should get always TRUE in console
// ------------------------------------------------

let myRanges = "0,71-140,34,142-216,20-30,7";

// is in tests
let casesIn = [
  [0, true],
  [25, true],
  [35, false],
  [200, true],
  [8, false]
];

for (const c of casesIn) {
  console.log(c[0], isInRanges(myRanges, c[0]) == c[1])
}

// is out tests
let casesOut = [
  [-2, true],
  [60, false],
  [300, true],
  [7, false]
];

for (const c of casesOut) {
  console.log(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}

I was curious about performance, and whether this would have any problems scaling up, so I decided to run some tests on JSBench.me (original code vs this revision). After running a couple of consecutive tests, it seems that the trimmed down version is somewhat faster.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
FiddlingAway
  • 1,598
  • 3
  • 14
  • 30
  • nice answer. JSBench shoud be corrected a little bit - because you never call your functions (!) :P (you only measure time of they declaration - not usage) - look here for example: https://jsbench.me/vnkhzab2d6/1 – Kamil Kiełczewski Jan 10 '23 at 22:53
  • 1
    I think this might be a little nicer: `const not = (fn) => (...args) => !fn(...args); const isOutOfRanges = not (isInRanges)`. This `not` should work for any predicate function, and it's a useful thing to keep in a personal utility library. – Scott Sauyet Jan 11 '23 at 16:25
  • @KamilKiełczewski There's console logging inside of the loop at the very bottom of the test cases, which is calling the function. I guess that's covering it. – FiddlingAway Jan 11 '23 at 18:10
  • @ScottSauyet This would work very well, if the two functions were the opposites, but I don't think that's the case here (unless I'm misreading the original problem). In fact, looking at my solution, I think my code is wrong - e.g, if I were to test `isOutOfRanges` with `[1, false]`, the test would return `false`. But 1 is smaller than the smallest range (if we consider `20-30` to be the smallest, both in the number of members for the range, and by being smaller than the smallest member of the range. But if we consider single numbers to be ranges as well (e.g. `0-0`), then I guess it's valid. – FiddlingAway Jan 11 '23 at 18:28
  • @FiddlingAway: I admit I didn't read very carefully, but I did make the assumption that single numbers were 0-lenth, inclusive ranges. It still looks right to me, but I may be missing something. Yes, this technique will only work if we are looking for the strictly inverted predicate. If that's not the case here, please ignore this. – Scott Sauyet Jan 11 '23 at 18:59