У меня есть диапазоны, описанные как строка
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).
Мой собственный ответ был бы следующим: jsfiddle.net/davidThomas/tyq63eL7, но я не могу понять, каков ваш требуемый результат/результат
@DavidThomas спасибо за ваш комментарий - я обновляю вопрос - он вам что-то объясняет?
Моя демонстрация возвращает логическое значение, так что я думаю, это можно считать ответом?
@DavidThomas, если вы предоставите в нем две функции (которые проходят рассматриваемые тесты) - я думаю, все будет в порядке
Можете ли вы четко объяснить, каким должен быть вход в функцию? И какой именно результат вы ожидаете от каждого входа? Хотя вполне возможно, что я упускаю что-то очевидное.
@DavidThomas Итак, для обеих функций: ввод представляет собой строку с диапазонами (const myRanges во фрагменте) и числом (num во фрагменте), которые мы хотим проверить. Вывод true или false.
Вот мой ответ (для будущих поколений) - а может быть у кого-то есть лучше?
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 да - вы правы - в тестовых примерах была ошибка - я меняю 34 на 35 - спасибо :)
Что, если бы мы сделали это .map(r => r.split('-')) вместо этого .map(r => r.match(/-/) ? r.split('-') : [r]) ? Есть ли недостатки (скорость, например)?
Больше игры в гольф: isOutOfRanges можно сократить до этого: const sorted = ranges.match(/\d+/g).map(Number); return num < Math.min(...sorted) || num > Math.max(...sorted); и сохранить 9 символов.
@FiddlingAway - вау - супер крутой совет ведьма по оптимизации кода split - я обновляю ответ
@FiddlingAway, можете ли вы добавить новый ответ с вашими улучшениями? (просто упомяните там, что вы улучшаете мой код) - я приму это
Конечно, только что опубликовал его с тестовой настройкой на JSBench.me.
Код, первоначально опубликованный @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
Я думаю, что это может быть немного лучше: const not = (fn) => (...args) => !fn(...args); const isOutOfRanges = not (isInRanges). Этот not должен работать для любой предикатной функции, и его полезно хранить в личной служебной библиотеке.
@KamilKiełczewski Внутри цикла в самом низу тестовых примеров есть консольная регистрация, которая вызывает функцию. Я думаю, это покрывает это.
@ScottSauyet Это сработало бы очень хорошо, если бы две функции были противоположными, но я не думаю, что здесь дело обстоит именно так (если только я неправильно истолковал исходную проблему). На самом деле, глядя на мое решение, я думаю, что мой код неверен — например, если бы я тестировал isOutOfRanges с помощью [1, false], тест вернул бы false. Но 1 меньше наименьшего диапазона (если мы считаем 20-30 наименьшим, как по количеству членов для диапазона, так и по тому, что он меньше наименьшего члена диапазона. Но если мы считаем, что отдельные числа являются диапазонами как ну (например, 0-0), тогда я думаю, что это действительно.
@FiddlingAway: я признаю, что не очень внимательно читал, но я сделал предположение, что одиночные числа были нулевыми, включая диапазоны. Он по-прежнему выглядит правильно для меня, но я могу что-то упустить. Да, этот прием будет работать, только если мы ищем строго инвертированный предикат. Если это не так, пожалуйста, игнорируйте это.
@MrSmith42 - Наконец-то я не застрял - я даю ответ на свой вопрос - что разрешено SO - я трачу некоторое время, чтобы найти решение этой проблемы, поэтому я публикую его для будущих чтений (чтобы сохранить они время)