Skip to content

Commit 69d8919

Browse files
committed
feat(util): 🎸 improve AvlSet iteration strategies
1 parent ce43424 commit 69d8919

File tree

3 files changed

+115
-7
lines changed

3 files changed

+115
-7
lines changed

src/util/trees/avl/AvlMap.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ export class AvlMap<K, V> implements Printable {
9898
}
9999

100100
public forEach(fn: (node: AvlNode<K, V>) => void): void {
101-
const root = this.root;
102-
if (!root) return;
103-
let curr = first(root);
101+
let curr = this.first();
102+
if (!curr) return;
104103
do fn(curr!);
105104
while ((curr = next(curr as HeadlessNode) as AvlNode<K, V> | undefined));
106105
}
@@ -115,7 +114,7 @@ export class AvlMap<K, V> implements Printable {
115114
public iterator0(): (() => undefined | AvlNode<K, V>) {
116115
let curr = this.first();
117116
return () => {
118-
if (!curr) return undefined;
117+
if (!curr) return;
119118
const value = curr;
120119
curr = next(curr as HeadlessNode) as AvlNode<K, V> | undefined;
121120
return value;

src/util/trees/avl/AvlSet.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,44 @@ export class AvlSet<V> implements Printable {
9191
}
9292

9393
public forEach(fn: (node: AvlSetNode<V>) => void): void {
94-
const root = this.root;
95-
if (!root) return;
96-
let curr = first(root);
94+
let curr = this.first();
95+
if (!curr) return;
9796
do fn(curr!);
9897
while ((curr = next(curr as HeadlessNode) as AvlSetNode<V> | undefined));
9998
}
10099

100+
public first(): AvlSetNode<V> | undefined {
101+
const root = this.root;
102+
return root ? first(root) : undefined;
103+
}
104+
105+
public readonly next = next;
106+
107+
public iterator0(): (() => undefined | AvlSetNode<V>) {
108+
let curr = this.first();
109+
return () => {
110+
if (!curr) return undefined;
111+
const value = curr;
112+
curr = next(curr as HeadlessNode) as AvlSetNode<V> | undefined;
113+
return value;
114+
};
115+
}
116+
117+
public iterator(): Iterator<AvlSetNode<V>> {
118+
const iterator = this.iterator0();
119+
return {
120+
next: () => {
121+
const value = iterator();
122+
const res = <IteratorResult<AvlSetNode<V>>>{value, done: !value};
123+
return res;
124+
},
125+
};
126+
}
127+
128+
public entries(): IterableIterator<AvlSetNode<V>> {
129+
return <any>{[Symbol.iterator]: () => this.iterator()};
130+
}
131+
101132
public toString(tab: string): string {
102133
return this.constructor.name + printTree(tab, [(tab) => print(this.root, tab)]);
103134
}

src/util/trees/avl/__tests__/AvlSet.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,81 @@ test('can store structs', () => {
6464
expect(set.has(new Struct(3, 3))).toBe(true);
6565
expect(set.has(new Struct(2, 3))).toBe(true);
6666
});
67+
68+
describe('.first()/next() iteration', () => {
69+
test('for empty map, returns finished iterator', () => {
70+
const tree = new AvlSet<string>();
71+
const entry = tree.first();
72+
expect(entry).toEqual(undefined);
73+
});
74+
75+
test('can iterate through map entries', () => {
76+
const tree = new AvlSet<number>();
77+
tree.add(1);
78+
tree.add(2);
79+
tree.add(3);
80+
const list: number[] = [];
81+
for (let entry = tree.first(); entry; entry = tree.next(entry)) {
82+
list.push(entry.k);
83+
}
84+
expect(list).toEqual([1, 2, 3]);
85+
});
86+
});
87+
88+
describe('.iterator0()', () => {
89+
test('for empty map, returns finished iterator', () => {
90+
const tree = new AvlSet<number>();
91+
const iterator = tree.iterator0();
92+
const entry = iterator();
93+
expect(entry).toEqual(undefined);
94+
});
95+
96+
test('can iterate through map entries', () => {
97+
const tree = new AvlSet<number>();
98+
tree.add(1);
99+
tree.add(2);
100+
tree.add(3);
101+
const list: number[] = [];
102+
const iterator = tree.iterator0();
103+
for (let entry = iterator(); entry; entry = iterator()) {
104+
list.push(entry.k);
105+
}
106+
expect(list).toEqual([1, 2, 3]);
107+
});
108+
});
109+
110+
describe('.iterator()', () => {
111+
test('for empty map, returns finished iterator', () => {
112+
const tree = new AvlSet<number>();
113+
const iterator = tree.iterator();
114+
const entry = iterator.next();
115+
expect(entry).toEqual({done: true, value: undefined});
116+
});
117+
118+
test('can iterate through map entries', () => {
119+
const tree = new AvlSet<number>();
120+
tree.add(1);
121+
tree.add(2);
122+
tree.add(3);
123+
const list: number[] = [];
124+
const iterator = tree.iterator();
125+
for (let entry = iterator.next(); !entry.done; entry = iterator.next()) {
126+
list.push(entry.value!.k);
127+
}
128+
expect(list).toEqual([1, 2, 3]);
129+
});
130+
});
131+
132+
describe('for...of iteration', () => {
133+
test('can iterate through map entries', () => {
134+
const tree = new AvlSet<number>();
135+
tree.add(1);
136+
tree.add(2);
137+
tree.add(3);
138+
const list: number[] = [];
139+
for (const entry of tree.entries()) {
140+
list.push(entry.k);
141+
}
142+
expect(list).toEqual([1, 2, 3]);
143+
});
144+
});

0 commit comments

Comments
 (0)