locutus
Version:
Locutus other languages' standard libraries to JavaScript for fun and educational purposes
43 lines (42 loc) • 1.8 kB
JavaScript
function normalizeReduceWhileStep(value) {
if (Array.isArray(value)) {
const tag = value[0];
if ((tag === 'cont' || tag === 'halt') && value.length >= 2) {
return [tag, value[1]];
}
}
else if (value && typeof value === 'object') {
const tag = value.tag;
if (tag === 'cont' || tag === 'halt') {
return [tag, value.value];
}
}
throw new TypeError("reduce_while(): reducer must return ['cont', value], ['halt', value], or { tag, value }");
}
export function reduce_while(values, initial, reducer) {
// discuss at: https://locutus.io/elixir/reduce_while/
// parity verified: Elixir 1.18
// original by: Kevin van Zonneveld (https://kvz.io)
// note 1: Mirrors Elixir Enum.reduce_while/3 using ['cont', acc] and ['halt', acc] control tuples.
// example 1: reduce_while([1, 2, 3, 4], 0, (acc, value) => (value < 4 ? ['cont', acc + value] : ['halt', acc]))
// returns 1: 6
// example 2: reduce_while([1, 3, 4, 6], null, (acc, value) => (value % 2 === 0 ? ['halt', value] : ['cont', acc]))
// returns 2: 4
// example 3: reduce_while([], 'done', (acc, _value) => ['cont', acc])
// returns 3: 'done'
if (typeof reducer !== 'function') {
throw new TypeError('reduce_while(): reducer must be a function');
}
if (!Array.isArray(values) || values.length === 0) {
return initial;
}
let accumulator = initial;
for (let i = 0; i < values.length; i++) {
const [tag, nextAccumulator] = normalizeReduceWhileStep(reducer(accumulator, values[i], i, values));
accumulator = nextAccumulator;
if (tag === 'halt') {
break;
}
}
return accumulator;
}