From e6380ebf8561c142db3317e1de8da34a17817dc6 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Sat, 8 Jun 2024 01:10:32 +0300 Subject: [PATCH] minor(vestjs-runtime): IsolateWalker.reduce --- packages/vestjs-runtime/src/IsolateWalker.ts | 19 +++++ .../src/__tests__/IsolateWalker.test.ts | 78 ++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/packages/vestjs-runtime/src/IsolateWalker.ts b/packages/vestjs-runtime/src/IsolateWalker.ts index f3a438ea4..be716a0fc 100644 --- a/packages/vestjs-runtime/src/IsolateWalker.ts +++ b/packages/vestjs-runtime/src/IsolateWalker.ts @@ -47,6 +47,25 @@ export function walk( } } +export function reduce( + startNode: TIsolate, + callback: (acc: T, isolate: TIsolate, breakout: CB) => T, + initialValue: T, + visitOnly?: VisitOnlyPredicate, +): T { + let acc = initialValue; + + walk( + startNode, + (node, breakout) => { + acc = callback(acc, node, breakout); + }, + visitOnly, + ); + + return acc; +} + // This function returns true if the given predicate function returns true for any Isolate object in the tree. // If visitOnly is provided, only Isolate objects that satisfy the predicate are visited. export function some( diff --git a/packages/vestjs-runtime/src/__tests__/IsolateWalker.test.ts b/packages/vestjs-runtime/src/__tests__/IsolateWalker.test.ts index 2a6e1208f..ba5d61792 100644 --- a/packages/vestjs-runtime/src/__tests__/IsolateWalker.test.ts +++ b/packages/vestjs-runtime/src/__tests__/IsolateWalker.test.ts @@ -1,5 +1,5 @@ import { TIsolate } from 'Isolate'; -import { walk } from 'IsolateWalker'; +import { walk, reduce } from 'IsolateWalker'; type WalkedNode = TIsolate<{ id: string }>; @@ -95,3 +95,79 @@ describe('walk', () => { }); }); }); + +describe('reduce', () => { + let node = {} as unknown as TIsolate<{ value: number }>; + beforeEach(() => { + node = { + data: { value: 1 }, + children: [ + { + data: { value: 2 }, + children: [ + { data: { value: 1 }, $type: 's' }, + { + data: { value: 2 }, + children: [{ data: { value: 0 } }, { data: { value: 1 } }], + }, + { data: { value: 1 }, $type: 's' }, + ], + }, + { data: { value: 0 } }, + ], + } as unknown as TIsolate<{ value: number }>; + }); + + it('Should return the accumulated value of the tree', () => { + const sum = reduce(node, (acc, isolate) => acc + isolate.data.value, 0); + expect(sum).toBe(8); + }); + + it('Should traverse the tree in a depth-first order', () => { + const visited: string[] = []; + reduce( + node, + (acc, isolate) => { + visited.push(isolate.data.value); + return acc; + }, + '', + ); + + expect(visited).toEqual([1, 0, 1, 2, 1, 2, 0, 1]); + }); + + describe('Breakout', () => { + it('Should stop the walk when breakout is called', () => { + const visited: Array = []; + reduce( + node, + (acc, isolate, breakout) => { + visited.push(isolate.data.value); + if (isolate.data.value === 2) { + breakout(); + } + return acc; + }, + '', + ); + + expect(visited).toEqual([1, 0, 1, 2]); + }); + }); + + describe('VisitOnly', () => { + it('Should only visit nodes that satisfy the predicate', () => { + const output = reduce( + node, + (acc, isolate) => { + return acc + isolate.data.value; + }, + 0, + isolate => isolate.$type === 's', + ); + + expect(output).toBe(2); + }); + }); +});