Skip to content

Commit 83da347

Browse files
authored
Merge pull request #46 from theSherwood/master
simple iterator implementation
2 parents 3936fb5 + 051ef85 commit 83da347

File tree

5 files changed

+132
-9
lines changed

5 files changed

+132
-9
lines changed

README.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Method returns reference to the inserted node
7575
let node = tree.insert(key, value)
7676
```
7777

78-
### Exist(key,value)
78+
### Exist(key, value)
7979
Method returns true if item {key, value} exists in the tree. <br/>
8080
Method may be useful if need to support unique items.
8181
```javascript
@@ -171,6 +171,29 @@ Clear tree
171171
tree.clear()
172172
```
173173

174+
### Iterate([interval, outputMapperFn])
175+
Returns an iterator (and iterable). <br/>
176+
Call `next` on the iterator to navigate to successor tree nodes and return the corresponding values. <br/>
177+
In the absence of a starting interval, the iterator will start with the lowest interval. <br/>
178+
```javascript
179+
let iterator = tree.iterate();
180+
let next = iterator.next().value;
181+
```
182+
Optional *outputMapperFn(value, key)* enables to map search results into custom defined output. <br/>
183+
Example:
184+
```javascript
185+
let iterator = tree.iterate([5,5], (value, key) => key);
186+
let next_key = iterator.next().value;
187+
```
188+
Supports `for .. of` syntax. <br/>
189+
Example:
190+
```javascript
191+
for (let key of tree.iterate([5,5], (value, key) => key)) {
192+
if (key[0] > 8) break;
193+
console.log(key);
194+
}
195+
```
196+
174197
## Documentation
175198
Documentation may be found here: [https://alexbol99.github.io/flatten-interval-tree](https://alexbol99.github.io/flatten-interval-tree/)
176199

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"types": "index.d.ts",
1212
"nyc": {
1313
"require": [
14-
"@babel/register"
14+
"@babel/register",
15+
"@babel/polyfill"
1516
],
1617
"sourceMap": false,
1718
"instrument": false

src/classes/intervalTree.js

+38-4
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,34 @@ class IntervalTree {
157157
this.tree_walk(this.root, (node) => visitor(node.item.key, node.item.value));
158158
}
159159

160-
/** Value Mapper. Walk through every node and map node value to another value
161-
* @param callback(value,key) - function to be called for each tree item
162-
*/
160+
/**
161+
* Value Mapper. Walk through every node and map node value to another value
162+
* @param callback(value,key) - function to be called for each tree item
163+
*/
163164
map(callback) {
164165
const tree = new IntervalTree();
165166
this.tree_walk(this.root, (node) => tree.insert(node.item.key, callback(node.item.value, node.item.key)));
166167
return tree;
167168
}
168169

170+
/**
171+
* @param {Interval} interval - optional if the iterator is intended to start from the beginning or end
172+
* @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output
173+
* @returns {Iterator}
174+
*/
175+
*iterate(interval, outputMapperFn = (value, key) => value === key ? key.output() : value) {
176+
let node;
177+
if (interval) {
178+
node = this.tree_search_nearest_forward(this.root, new Node(interval));
179+
} else if (this.root) {
180+
node = this.local_minimum(this.root);
181+
}
182+
while (node) {
183+
yield outputMapperFn(node.item.value, node.item.key);
184+
node = this.tree_successor(node);
185+
}
186+
}
187+
169188
recalc_max(node) {
170189
let node_current = node;
171190
while (node_current.parent != null) {
@@ -398,6 +417,21 @@ class IntervalTree {
398417
}
399418
}
400419

420+
tree_search_nearest_forward(node, search_node) {
421+
let best;
422+
let curr = node;
423+
while (curr && curr != this.nil_node) {
424+
if (curr.less_than(search_node)) {
425+
if (curr.intersect(search_node) && (!best || curr.less_than(best))) best = curr;
426+
curr = curr.right;
427+
} else {
428+
if (!best || curr.less_than(best)) best = curr;
429+
curr = curr.left;
430+
}
431+
}
432+
return best || null;
433+
}
434+
401435
// Original search_interval method; container res support push() insertion
402436
// Search all intervals intersecting given one
403437
tree_search_interval(node, search_node, res) {
@@ -595,7 +629,7 @@ class IntervalTree {
595629
}
596630
height += heightLeft;
597631
return height;
598-
};
632+
}
599633
};
600634

601635
export default IntervalTree;

test/intervalTree.js

+66-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {Interval} from "../index";
99
// import IntervalTree from '../dist/interval-tree.esm';
1010

1111
describe('#IntervalTree', function() {
12-
it('Create new instanse of IntervalTree ', function () {
12+
it('Create new instance of IntervalTree ', function () {
1313
let tree = new IntervalTree();
1414
expect(tree).to.be.an.instanceof(IntervalTree);
1515
});
@@ -310,4 +310,69 @@ describe('#IntervalTree', function() {
310310
const resp2 = tree.search([4,11]);
311311
expect(resp2).to.deep.equal([[5,7],[7,8]]);
312312
})
313+
describe('##Iterator', function() {
314+
it('May find first intersecting interval', function () {
315+
let tree = new IntervalTree();
316+
let ints = [[6,8],[1,3],[5,12],[1,1],[5,7]];
317+
for (let int of ints) tree.insert(int);
318+
let iterator = tree.iterate([3,8]);
319+
expect(iterator.next().value).to.deep.equal([1,3]);
320+
});
321+
it('May find first forward interval when there is no intersection', function () {
322+
let tree = new IntervalTree();
323+
let ints = [[6,8],[1,3],[5,12],[1,1],[5,7]];
324+
for (let int of ints) tree.insert(int);
325+
let iterator = tree.iterate([4,4]);
326+
expect(iterator.next().value).to.deep.equal([5,7]);
327+
});
328+
it('May find first interval when no interval is passed', function () {
329+
let tree = new IntervalTree();
330+
let ints = [[6,8],[1,3],[5,12],[1,1],[5,7]];
331+
for (let int of ints) tree.insert(int);
332+
let iterator = tree.iterate();
333+
expect(iterator.next().value).to.deep.equal([1,1]);
334+
});
335+
it('Supports iteration with `for .. of` syntax', function () {
336+
let tree = new IntervalTree();
337+
let ints = [[6,8],[1,3],[5,12],[1,1],[5,7]];
338+
for (let int of ints) tree.insert(int);
339+
let results = [];
340+
for (let val of tree.iterate([4,4])) {
341+
results.push(val);
342+
}
343+
expect(results).to.deep.equal([[5,7],[5,12],[6,8]]);
344+
});
345+
it('Returns `{done: true}` when it reaches the end', function () {
346+
let tree = new IntervalTree();
347+
let ints = [[6,8],[1,3],[5,12],[1,1],[5,7]];
348+
for (let int of ints) tree.insert(int);
349+
let iterator = tree.iterate([8,8]);
350+
expect(iterator.next().value).to.deep.equal([5,12]);
351+
expect(iterator.next().value).to.deep.equal([6,8]);
352+
let res = iterator.next();
353+
expect(res.value).to.equal(undefined);
354+
expect(res.done).to.equal(true);
355+
});
356+
it('Supports custom transformed objects', function () {
357+
const composers = [
358+
{name: "Ludwig van Beethoven", period: [1770,1827]},
359+
{name: "Johann Sebastian Bach", period: [1685, 1750]},
360+
{name: "Wolfgang Amadeus Mozart", period: [1756, 1791]},
361+
{name: "Johannes Brahms", period: [1833, 1897]},
362+
{name: "Richard Wagner", period: [1813, 1883]},
363+
{name: "Claude Debussy", period: [1862, 1918]},
364+
{name: "Pyotr Ilyich Tchaikovsky", period: [1840, 1893]},
365+
{name: "Frédéric Chopin", period: [1810, 1849]},
366+
{name: "Joseph Haydn", period: [1732, 1809]},
367+
{name: "Antonio Vivaldi", period: [1678, 1741]}
368+
];
369+
const tree = new IntervalTree();
370+
for (let composer of composers)
371+
tree.insert(composer.period, composer.name);
372+
let iterator = tree.iterate([1600, 1700],
373+
(name, period) => {return `${name} (${period.low}-${period.high})`});
374+
expect(iterator.next().value).to.equal("Antonio Vivaldi (1678-1741)");
375+
expect(iterator.next().value).to.equal("Johann Sebastian Bach (1685-1750)");
376+
});
377+
});
313378
});

0 commit comments

Comments
 (0)