Это неудобно делать в .reduce
, потому что он проходит через весь массив . Если мы сделаем наивную реализацию, вы увидите проблему:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0)
console.log(sum(arr, 0))
Теперь мы делаем проверку правильно - num==x
вернет true
, когда x
равно нулю (значение num
). Однако результат неверен, потому что он возвращает только true
один раз , но при любой другой итерации он все равно true
. И вот то же самое с дополнительным журналом, описывающим каждый шаг процесса:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
const boolCheck = num==x;
const result = boolCheck ? total : total+x;
console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);
return result;
}, 0)
console.log(sum(arr, 0))
Итак, вам нужно добавить некоторый флаг, который сохраняется между итерациями, чтобы он не терялся.
Один из вариантов - иметь внешний флаг которые вы меняете в обратном вызове reduce
:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
let finished = false;
return arr.reduce((total, x) => {
if(x === num)
finished = true;
return finished ? total : total+x;
}, 0)
}
console.log(sum(arr, 0))
В качестве альтернативы вы можете использовать этот флаг внутри обратного вызова reduce
и передавать его между вызовами. В конце концов, он работает так же, но делает функцию обратного вызова чистой. Ценой какой-то неортодоксальной конструкции:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
return arr.reduce(({total, finished}, x) => {
if(x === num)
finished = true;
total = finished ? total : total+x;
return {total, finished};
}, {total: 0, finished: false})
.total
}
console.log(sum(arr, 0))
Если вы хотите использовать reduce
, но можете использовать другие методы, то вы можете использовать Array#indexOf
, чтобы найти первый экземпляр значения и Array#slice
массив, содержащий любое значение вплоть до целевого значения:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
const endIndex = arr.indexOf(num);
return arr.slice(0, endIndex)
.reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))
Или в виде одного связанного выражения:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
.slice(0, arr.indexOf(num))
.reduce((total, x)=> total+x, 0);
console.log(sum(arr, 0))
Другие библиотеки могут иметь операцию takeUntil
или takeWhile
, которая даже ближе к тому, что вы хотите - она дает вам массив из начиная с до заданного значения или условия. Затем вы можете уменьшить результат этого.
Вот пример этого с использованием Lodash#takeWhile
Используя здесь цепочку, Loda sh выполнит ленивую оценку , поэтому он будет go пройти через массив только один раз, вместо того, чтобы сканировать один раз, чтобы найти конечный индекс, и снова пройти через массив для его суммирования.
const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
.takeWhile(x => x !== num)
.reduce((total, x)=>total+x, 0)
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
В качестве примечания, если вы используете , используя Loda sh, то вы также можете использовать _.sum()
. Выше я не просто проиллюстрировал, как выглядит generi c takeUntil
/ takeWhile
.
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
.takeWhile(x => x !== num)
.sum()
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>