This repository has been archived by the owner on Jul 30, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathiterator.ts
156 lines (139 loc) · 3.71 KB
/
iterator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { ArrayLike } from './interfaces';
import { HIGH_SURROGATE_MIN, HIGH_SURROGATE_MAX } from './string';
import './Symbol';
export interface IteratorResult<T> {
done: boolean;
value: T;
}
export interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
export interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
export interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>;
}
const staticDone: IteratorResult<any> = { done: true, value: undefined };
/**
* A class that provides "shims" an iterator interface on array like
* objects.
*/
export class ShimIterator<T> {
private _list: ArrayLike<T>;
private _nextIndex: number = -1;
private _nativeIterator: Iterator<T>;
constructor(list: ArrayLike<T> | Iterable<T>) {
if (isIterable(list)) {
this._nativeIterator = list[Symbol.iterator]();
}
else {
this._list = list;
}
};
/**
* Return the next iteration result for the Iterator
*/
next(): IteratorResult<T> {
if (this._nativeIterator) {
return this._nativeIterator.next();
}
if (!this._list) {
return staticDone;
}
if (++this._nextIndex < this._list.length) {
return {
done: false,
value: this._list[this._nextIndex]
};
}
return staticDone;
};
[Symbol.iterator](): IterableIterator<T> {
return this;
}
}
/**
* A type guard for checking if something has an Iterable interface
*
* @param value The value to type guard against
*/
export function isIterable(value: any): value is Iterable<any> {
return value && typeof value[Symbol.iterator] === 'function';
}
/**
* A type guard for checking if something is ArrayLike
*
* @param value The value to type guard against
*/
export function isArrayLike(value: any): value is ArrayLike<any> {
return value && typeof value.length === 'number';
}
/**
* Returns the iterator for an object
*
* @param iterable The iterable object to return the iterator for
*/
export function get<T>(iterable: Iterable<T> | ArrayLike<T>): Iterator<T> | undefined {
if (isIterable(iterable)) {
return iterable[Symbol.iterator]();
}
else if (isArrayLike(iterable)) {
return new ShimIterator(iterable);
}
};
export interface ForOfCallback<T> {
/**
* A callback function for a forOf() iteration
*
* @param value The current value
* @param object The object being iterated over
* @param doBreak A function, if called, will stop the iteration
*/
(value: T, object: Iterable<T> | ArrayLike<T> | string, doBreak: () => void): void;
}
/**
* Shims the functionality of `for ... of` blocks
*
* @param iterable The object the provides an interator interface
* @param callback The callback which will be called for each item of the iterable
* @param thisArg Optional scope to pass the callback
*/
export function forOf<T>(iterable: Iterable<T> | ArrayLike<T> | string, callback: ForOfCallback<T>, thisArg?: any): void {
let broken = false;
function doBreak() {
broken = true;
}
/* We need to handle iteration of double byte strings properly */
if (!isIterable(iterable) && typeof iterable === 'string') {
const l = iterable.length;
for (let i = 0; i < l; ++i) {
let char = iterable[i];
if ((i + 1) < l) {
const code = char.charCodeAt(0);
if ((code >= HIGH_SURROGATE_MIN) && (code <= HIGH_SURROGATE_MAX)) {
char += iterable[++i];
}
}
callback.call(thisArg, char, iterable, doBreak);
if (broken) {
return;
}
}
}
else {
const iterator = get(iterable);
if (iterator) {
let result = iterator.next();
while (!result.done) {
callback.call(thisArg, result.value, iterable, doBreak);
if (broken) {
return;
}
result = iterator.next();
}
}
}
}