From eec483093df7075cc94f686b1440f0dcfd76f466 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Fri, 11 Jul 2025 18:37:47 -0300 Subject: src/sjs.mjs: Add reductions() --- src/sjs.mjs | 29 ++++++++++++++++++++++--- tests/sjs.mjs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/sjs.mjs b/src/sjs.mjs index 2c9d551..e9da7fc 100644 --- a/src/sjs.mjs +++ b/src/sjs.mjs @@ -294,9 +294,8 @@ export const reduced = x => new Reduced(x); const reduceRec = (coll, fn, acc, index) => isReduced(acc) ? acc.value : - index === coll.length - ? acc - : reduceRec( + index === coll.length ? acc : + reduceRec( coll, fn, fn(acc, coll[index], index, coll), @@ -308,6 +307,30 @@ export const reduce = (coll, fn, init) => ? reduceRec(coll.slice(1), fn, coll[0], 0) : reduceRec(coll, fn, init, 0); +const reductionsRec = (coll, fn, acc, index, steps) => { + if (isReduced(acc)) { + return steps.concat(acc.value); + } + + if (index === coll.length) { + return steps; + } + + const newAcc = fn(acc, coll[index], index, coll); + return reductionsRec( + coll, + fn, + newAcc, + index + 1, + steps.concat(newAcc), + ); +}; + +export const reductions = (coll, fn, init) => + init === undefined + ? reductionsRec(coll.slice(1), fn, coll[0], 0, [coll[0]]) + : reductionsRec(coll, fn, init, 0, []); + export const mapValues = (obj, fn) => Object.keys(obj).reduce( (o, k) => ({ diff --git a/tests/sjs.mjs b/tests/sjs.mjs index c8f52dd..9376429 100644 --- a/tests/sjs.mjs +++ b/tests/sjs.mjs @@ -32,6 +32,8 @@ import { reduced, reduceRec, reduce, + reductionsRec, + reductions, mapValues, repeat, runTests, @@ -958,6 +960,72 @@ const test_reduce = t => { }); }; +const test_reductionsRec = t => { + t.start("reductionsRec()"); + + t.testing("return the value if isReduced()", () => { + t.assertEq( + reductionsRec(null, null, reduced("abc"), null, []), + ["abc"], + ); + }); + + t.testing("the bare value if at the end of the collection", () => { + t.assertEq(reductionsRec([], null, null, 0, "ret"), "ret"); + t.assertEq(reductionsRec([1, 2], null, null, 2, "ret"), "ret"); + }); + + t.testing("recurse while applying function", () => { + const expected = [{ + acc: -5, + el: 4, + index: 3, + coll: [1, 2, 3, 4, 5], + ret: -1, + }, { + acc: -1, + el: 5, + index: 4, + coll: [1, 2, 3, 4, 5], + ret: 4, + }]; + const calls = []; + const fn = (acc, el, index, coll) => { + const ret = acc + el; + calls.push({ + acc, + el, + index, + coll, + ret, + }); + return ret; + }; + const coll = [1, 2, 3, 4, 5]; + t.assertEq(reductionsRec(coll, fn, -5, 3, []), [-1, 4]); + t.assertEq(calls, expected); + }); +}; + +const test_reductions = t => { + t.start("reductions()"); + + const add = (a, b) => a + b; + + + t.testing("with and without initial values", () => { + t.assertEq(reductions([1, 1, 1, 1], add), [1, 2, 3, 4]); + t.assertEq(reductions([1, 1, 1], add, 0), [1, 2, 3]); + t.assertEq(reductions([1, 1, 1], add, 1), [2, 3, 4]); + t.assertEq(reductions([1, 2, 3], add), [1, 3, 6]); + }); + + t.testing("the last values corresponds to reduce()", () => { + const arr = [1, 2, 3]; + t.assertEq(last(reductions(arr, add)), reduce(arr, add)); + }); +}; + const test_mapValues = t => { t.start("mapValues()"); @@ -1021,6 +1089,8 @@ runTests([ test_reduced, test_reduceRec, test_reduce, + test_reductionsRec, + test_reductions, test_mapValues, test_repeat, ]); -- cgit v1.2.3