Проверьте, находится ли число в диапазонах, описанных как строка

У меня есть диапазоны, описанные как строка

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

(не отсортировано; одно число, например, 34 означает диапазон 34-34).

  • Как проверить, что число num находится в некотором диапазоне (из заданных диапазонов)
  • Как проверить, что число меньше наименьшего диапазона или больше наибольшего диапазона?

Это инверсия этого вопрос.

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.info(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.info(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}

Решением будут две функции (см. сниппет), которые возвращают true/false и проходят все тест-кейсы (на консоли всегда должно быть true).

@MrSmith42 - Наконец-то я не застрял - я даю ответ на свой вопрос - что разрешено SO - я трачу некоторое время, чтобы найти решение этой проблемы, поэтому я публикую его для будущих чтений (чтобы сохранить они время)

Kamil Kiełczewski 10.01.2023 18:31

Мой собственный ответ был бы следующим: jsfiddle.net/davidThomas/tyq63eL7, но я не могу понять, каков ваш требуемый результат/результат

David Thomas 10.01.2023 18:36

@DavidThomas спасибо за ваш комментарий - я обновляю вопрос - он вам что-то объясняет?

Kamil Kiełczewski 10.01.2023 18:38

Моя демонстрация возвращает логическое значение, так что я думаю, это можно считать ответом?

David Thomas 10.01.2023 18:40

@DavidThomas, если вы предоставите в нем две функции (которые проходят рассматриваемые тесты) - я думаю, все будет в порядке

Kamil Kiełczewski 10.01.2023 18:41

Можете ли вы четко объяснить, каким должен быть вход в функцию? И какой именно результат вы ожидаете от каждого входа? Хотя вполне возможно, что я упускаю что-то очевидное.

David Thomas 10.01.2023 18:43

@DavidThomas Итак, для обеих функций: ввод представляет собой строку с диапазонами (const myRanges во фрагменте) и числом (num во фрагменте), которые мы хотим проверить. Вывод true или false.

Kamil Kiełczewski 10.01.2023 18:50
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
Оптимизация React Context шаг за шагом в 4 примерах
Оптимизация React Context шаг за шагом в 4 примерах
При использовании компонентов React в сочетании с Context вы можете оптимизировать рендеринг, обернув ваш компонент React в React.memo сразу после...
Интервьюер: Почему '[] instanceof Object' возвращает "true"?
Интервьюер: Почему '[] instanceof Object' возвращает "true"?
Все мы знаем, что [] instanceof Array возвращает true, но почему [] instanceof Object тоже возвращает true?
Абстрактное синтаксическое дерево (AST) и как оно работает с ReactJS
Абстрактное синтаксическое дерево (AST) и как оно работает с ReactJS
Абстрактное синтаксическое дерево (AST) - это древовидная структура данных, которая представляет структуру и иерархию исходного кода на языке...
1
7
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вот мой ответ (для будущих поколений) - а может быть у кого-то есть лучше?

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.info(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.info(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}

Ваш фрагмент, кажется, имеет ошибку проверки -> 34 false..

Keith 10.01.2023 18:10

@Keith да - вы правы - в тестовых примерах была ошибка - я меняю 34 на 35 - спасибо :)

Kamil Kiełczewski 10.01.2023 18:21

Что, если бы мы сделали это .map(r => r.split('-')) вместо этого .map(r => r.match(/-/) ? r.split('-') : [r]) ? Есть ли недостатки (скорость, например)?

FiddlingAway 10.01.2023 22:21

Больше игры в гольф: isOutOfRanges можно сократить до этого: const sorted = ranges.match(/\d+/g).map(Number); return num < Math.min(...sorted) || num > Math.max(...sorted); и сохранить 9 символов.

FiddlingAway 10.01.2023 22:37

@FiddlingAway - вау - супер крутой совет ведьма по оптимизации кода split - я обновляю ответ

Kamil Kiełczewski 10.01.2023 22:38

@FiddlingAway, можете ли вы добавить новый ответ с вашими улучшениями? (просто упомяните там, что вы улучшаете мой код) - я приму это

Kamil Kiełczewski 10.01.2023 22:41

Конечно, только что опубликовал его с тестовой настройкой на JSBench.me.

FiddlingAway 10.01.2023 23:19
Ответ принят как подходящий

Код, первоначально опубликованный @KamilKiełczewski, можно немного урезать, чтобы он выглядел так.

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.info(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.info(c[0], isOutOfRanges(myRanges, c[0]) == c[1])
}

Мне было интересно узнать о производительности и о том, будут ли проблемы с масштабированием, поэтому я решил провести несколько тестов на JSBench.me (исходный код и эта версия). После запуска пары последовательных тестов кажется, что урезанная версия работает несколько быстрее.

Хороший ответ. JSBench нужно немного подправить - потому что вы никогда не вызываете свои функции (!) :P (вы измеряете только время их объявления, а не использования) - смотрите здесь например: jsbench.me/vnkhzab2d6/1

Kamil Kiełczewski 10.01.2023 23:53

Я думаю, что это может быть немного лучше: const not = (fn) => (...args) => !fn(...args); const isOutOfRanges = not (isInRanges). Этот not должен работать для любой предикатной функции, и его полезно хранить в личной служебной библиотеке.

Scott Sauyet 11.01.2023 17:25

@KamilKiełczewski Внутри цикла в самом низу тестовых примеров есть консольная регистрация, которая вызывает функцию. Я думаю, это покрывает это.

FiddlingAway 11.01.2023 19:10

@ScottSauyet Это сработало бы очень хорошо, если бы две функции были противоположными, но я не думаю, что здесь дело обстоит именно так (если только я неправильно истолковал исходную проблему). На самом деле, глядя на мое решение, я думаю, что мой код неверен — например, если бы я тестировал isOutOfRanges с помощью [1, false], тест вернул бы false. Но 1 меньше наименьшего диапазона (если мы считаем 20-30 наименьшим, как по количеству членов для диапазона, так и по тому, что он меньше наименьшего члена диапазона. Но если мы считаем, что отдельные числа являются диапазонами как ну (например, 0-0), тогда я думаю, что это действительно.

FiddlingAway 11.01.2023 19:28

@FiddlingAway: я признаю, что не очень внимательно читал, но я сделал предположение, что одиночные числа были нулевыми, включая диапазоны. Он по-прежнему выглядит правильно для меня, но я могу что-то упустить. Да, этот прием будет работать, только если мы ищем строго инвертированный предикат. Если это не так, пожалуйста, игнорируйте это.

Scott Sauyet 11.01.2023 19:59

Другие вопросы по теме