#javascript #conditional-statements
#javascript #условные операторы
Вопрос:
В приведенном ниже примере кода я хочу отфильтровать numbersArray на основе разных интервалов. Интервал определяется комбинацией 2 массивов с нижней и верхней границей указанного интервала. Как мне определить совпадения или несоответствия, как показано ниже?
const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
Если код работает, следующий тест должен возвращать true:
matches == [2, 5, 9];
nonmatches == [1, 3, 4, 6, 7, 8, 10];
Предположим, что массивы содержат более 1000 элементов и что числа не соответствуют никакому шаблону.
Ниже приведен менее читаемый, но более реалистичный сценарий.
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);
}
Комментарии:
1. . Вы хотите найти способ сравнить различия или совпадения между младшими и старшими значениями?
2. Будут ли связанные массивы всегда иметь одинаковую длину? Имеют ли они дело с -Infinity Infiity? Насколько вы заботитесь о производительности?
3. Это всегда будет интервал, который мы ищем. Поэтому, если массивы имеют разную длину, это будет ошибкой. Интервал — это комбинация определенного индекса, например
lowerBound[index]
, tohigherBound[index]
. Что касается производительности: она должна быть достаточно быстрой, чтобы не было видимой задержки в пользовательском интерфейсе. Таким образом, максимум 10 мс и чем меньше, тем лучше.4. Я попытался понять вашу постановку задачи о том, что такое интервал, как границы относятся к интервалу и каким должно быть предполагаемое поведение между интервалом и numbersArray . Но я, честно говоря, не могу этого понять, я думаю, что проблема недостаточно определена, и читатели догадываются, каково предполагаемое поведение.
Ответ №1:
Я собираюсь предположить, что мы можем немного изменить структуру данных, потому что с этим довольно неудобно работать:
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
Если элементы с одинаковыми индексами должны быть вместе, тогда давайте просто сделаем это:
const bound = [[1, 3], [4, 6], [8, 10]];
Будет bound
позже.
Теперь давайте создадим функцию curried, которая проверяет n
, если a < n amp;amp; n < b
:
const between = (a, b) => n => a < n amp;amp; n < b;
const x = between(1, 3);
const y = between(4, 6);
x(1); // false
x(2); // true
y(1); // false
y(5); // true
Теперь давайте создадим еще одну функцию curried, которая проверяет n
, возвращает ли хотя бы одна функция true
:
const or = (...fns) => n => fns.some(fn => fn(n));
const check = or(x, y);
check(1); // false
check(2); // true
check(5); // true
Теперь мы преобразуем bound
в функцию или после того, как мы преобразовали каждую пару в функцию between:
const bound = [[1, 3], [4, 6], [8, 10]];
const check = or(...bound.map(([a, b]) => between(a, b)));
check
теперь функция, которая принимает n
и возвращает true
, если n
находится между 1 и 3 или между 4 и 6, …
const between = (a, b) => n => a < n amp;amp; 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}]`);
Комментарии:
1. Я согласен с предикатами и, безусловно, с неуклюжестью структуры данных, которая просто вызывает много вопросов. Хорошая работа по структурированию различных методов 🙂
2.
.reduce((results, n) => (results[ check(n)].push(n), results), [[],[]])
reduce
в последнем фрагменте будет компактная версия. (Он меняет местами результирующееmatch
иnomatch
.)3. Это очень удобочитаемое решение и очень удобно в сочетании с zip-решением @3limin4t0r для неудобной структуры данных. Это, безусловно, самое быстрое решение для предоставленных тестовых данных, а также приятное разделение проблем.
Ответ №2:
Я думаю, что сначала извлек бы значения нижнего и верхнего пределов в список предикатов, а затем просто повторил бы их для каждого числа в массиве. В зависимости от того, совпадает ли одно из них, оно либо попадает в результат совпадения, либо в результат несоответствия.
function filtered( array, lower, upper) {
const predicates = lower.map( (v, i) => (value) => value > v amp;amp; value < upper[i] );
return array.reduce( (agg, cur) => {
if (predicates.some( predicate => predicate(cur) )) {
agg[0].push(cur);
} else {
agg[1].push(cur);
}
return agg;
}, [[],[]]);
}
function simpleTest() {
const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
const [matches, nonmatches] = filtered( numbersArray, lowerBound, higherBound );
console.log( 'matches' );
console.log( matches );
console.log( 'matches' );
console.log( nonmatches );
}
function suggestedTest() {
// with suggested test
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);
}
const [matches, nonmatches] = filtered( numbersArray, lowerBound, higherBound );
console.log( 'matches' );
console.log( matches );
console.log( 'matches' );
console.log( nonmatches );
}
console.log('basic');
simpleTest();
console.log('suggested');
suggestedTest();
Лично я бы также проверил, имеют ли нижние и верхние массивы одинаковую длину, но вопрос, похоже, не определяет поведение для этого сценария. Я также не уверен, что должно произойти в случае перекрытия диапазонов, но все они не указаны
Ответ №3:
Немного изменил свой подход, чтобы избежать второго цикла фильтрации. Это просто случай проверки каждого числа в массиве на соответствие неявной паре lowerBound
и higherBound
. Вы можете использовать переменную итерации в некоторых, чтобы добиться этого достаточно лаконично, предполагая, что массивы были правильно отформатированы заранее и нет свободных концов.
const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
let matches = [];
let nonMatches = [];
numbersArray.forEach(num => {
const matched = lowerBound.some((bound, i) => {
return num > bound amp;amp; num < higherBound[i];
});
matched ? matches.push(num) : nonMatches.push(num);
});
console.log(matches, nonMatches);
Комментарии:
1. По какой причине вы не пошли
reduce
? Кажется более естественным использоватьreduce
over aforEach
, когда это приводит к данным послеforEach
цикла2. В наши дни я стараюсь избегать сокращения , если я не делаю именно то, что указано в tin (т.Е. Уменьшаю итерацию до одного значения / массива / объекта). Ни одна из приведенных здесь данных фактически не изменилась, поэтому я не думаю, что это хороший вариант использования.
Ответ №4:
Этот подход довольно прост. Сначала zip lowerBound
и higherBound
вместе для удобства (это необязательно). Затем для каждого number
in numbersArray
проверьте, есть ли хотя бы одно совпадение (с использованием some
) между нижней и верхней границей. Добавьте число в массив «совпадение» или «несоответствие».
const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
// zip the lower and higher bounds together
const bounds = lowerBound.map((_, i) => [lowerBound[i], higherBound[i]]);
const result = {true: [], false: []};
for (const number of numbersArray) {
const match = bounds.some(([low, high]) => low < number amp;amp; number < high);
result[match].push(number);
}
console.log("matches:", ...result[true]);
console.log("non-matches:", ...result[false]);
Ответ №5:
Это то, что вы хотите?
Я использую reduce для создания массива совпадений, в который я помещаю новый элемент, когда он совпадает (между нижним и верхним), а для несовпадений — просто фильтр чисел в numbersArray, которые не представлены в совпадениях.
const numbersArray = [1,2,3,4,5,6,7,8,9,10];
const lowerBound = [1, 4, 8];
const higherBound = [3, 6, 10];
const matches = lowerBound.reduce((acc, val, i) => {
return [
...acc,
...numbersArray.filter(o => !acc.includes(o) amp;amp; o > lowerBound[i] amp;amp; o < higherBound[i])
]
}, []);
const nonmatches = numbersArray.filter(o => !matches.includes(o));
console.log(matches);
console.log(nonmatches);
Комментарии:
1. Имея в виду, что это приводит к дублированию в
matched
массиве, когда границы перекрываются.2. Обратите внимание, что для этого примера этот ответ должен повторяться 3 раза
numbersArray
. Или обобщенный: его нужно повторить N разnumbersArray
, где N — длинаlowerBound
(илиhigherBound
) массива.