-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimeWindow.js
363 lines (320 loc) · 13.7 KB
/
timeWindow.js
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/* Copyright 2019 ScaleOut Software, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const _sourceArray = Symbol('_sourceArray');
/**
* Iterable class representing a collection of elements that fall within a time window.
* @hideconstructor
*/
class TimeWindow {
constructor(sourceArray, start, end, isEndInclusive, sourceIndex, sourceCount) {
/** @member {number} - Start time (inclusive) of the window, expressed as milliseconds elapsed since January 1, 1970 00:00:00 UTC.*/
this.start = start;
/** @member {number} - End time of the window, expressed as milliseconds elapsed since January 1, 1970 00:00:00 UTC. The object's inclusiveEnd field indicates whether the end is inclusive or exclusive.*/
this.end = end;
/** @member {bool} - Whether the end time is inclusive (true) or exclusive (false) */
this.isEndInclusive = isEndInclusive;
this[_sourceArray] = sourceArray;
this.sourceIndex = sourceIndex;
this.length = sourceCount;
}
*[Symbol.iterator]() {
const sourceArray = this[_sourceArray];
for (let i = 0; i < this.length; i++) {
yield sourceArray[i + this.sourceIndex];
}
}
/**
* Get the start time (inclusive) of the window as a Date object.
* @type {Date}
* @readonly
*/
get startDate() {
return new Date(this.start);
}
/**
* Get the end time of the window as a Date object. The object's inclusiveEnd
* field indicates whether the end is inclusive or exclusive.
* @type {Date}
* @readonly
*/
get endDate() {
return new Date(this.end);
}
/**
* Get the duration of the window in milliseconds.
* @type {number}
* @readonly
*/
get durationMillis() {
return this.end - this.start;
}
/**
* Returns elements in the window as an Array.
* @returns {Array} The window's elements as a new Array.
*/
toArray() {
//return this[_sourceArray].slice(this.sourceIndex, (this.sourceIndex + this.sourceCount));
// Relies iterator and doesn't care about implementation... thus more maintainable:
return Array.from(this);
}
/**
* Function that produces an element for the new Array, taking three arguments:
*
* @callback mapCallback
* @param {any} currentValue - The current element in the TimeWindow being processed.
* @param {number} [index] - The index of the current element being processed in the array.
* @param {TimeWindow} [window] - The TimeWindow that map() was called upon.
* @returns {any} Transformed element.
*/
/**
* Creates a new array with the results of calling a provided function
* on every element in the calling TimeWindow instance.
* @param {mapCallback} callbackfn - Function that produces an element of the new Array.
* @param {any} [thisArg] - Optional. Value to use as <tt>this</tt> when executing callback.
* @returns {Array} A new array with each element being the result of the callback function.
*/
map(callbackfn, thisArg) {
// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
const arr = [];
let k = 0;
for (const elem of this) {
// Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing the window element, k, and the source TimeWindow.
const mappedValue = callbackfn.call(T, elem, k, this);
arr.push(mappedValue);
k++;
}
return arr;
}
/**
* Predicate used to test each element the TimeWindow. Return true if the element satisifies the
* condition, false otherwise. Three arguments are provided to this callback:
*
* @callback predicateCallback
* @param {any} currentValue - The current element in the TimeWindow being tested.
* @param {number} [index] - The index of the current element being processed in the array.
* @param {TimeWindow} [window] - The TimeWindow being evaluated.
* @returns {boolean} true if the element satisfies the condition, false otherwise.
*/
/**
* Creates a new array containing elements from the TimeWindow that pass the
* test implemented by the provided function.
* @param {predicateCallback} callbackfn - Function is a predicate used to test each element of the array. Return true to keep the element, false otherwise.
* @param {any} [thisArg] - Optional. Value to use as this when executing the callback.
* @returns {Array} A new array with the elements that pass the test. If no elements pass the test, an empty array will be returned.
*/
filter(callbackfn, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
const arr = [];
let k = 0;
for (const elem of this) {
if (callbackfn.call(T, elem, k, this)) {
arr.push(elem);
}
k++;
}
return arr;
}
/**
* Reducer function to execute on each element in the TimeWindow, taking four arguments:
*
* @callback reduceCallback
* @param {any} accumulator - The accumulator accumulates the callback's return values; it is the accumulated value previously returned in the last invocation of the callback, or initialValue, if supplied.
* @param {any} currentValue - The current element in the TimeWindow being processed.
* @param {number} [index] - The index of the current element being processed in the array.
* @param {TimeWindow} [window] - The TimeWindow that reduce() was called upon.
* @returns {any} The accumulating value that is eventually returned to the reduce() caller. This returned value is assigned to the accumulator, whose value is remembered across each iteration throughout the window and ultimately becomes the final, single resulting value.
*/
/**
* Executes the provided reducer function on each member of the TimeWindow, resulting in a single output value.
* @param {reduceCallback} callbackfn - Function that executes each element in the TimeWindow and returns an accumulating value.
* @param {any} [initialValue] - Optional. Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the window will be used. Calling reduce() on an empty window without an initial value is an error.
* @returns {any} The accumulated value that results from the reduction.
*/
reduce(callbackfn/*, initialValue*/) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let k = 0;
let accumulator = undefined;
let haveInitialValue = false;
if (arguments.length >= 2) {
accumulator = arguments[1];
haveInitialValue = true;
} else {
haveInitialValue = false;
}
for (const elem of this) {
if (k === 0 && !haveInitialValue) {
// Don't invoke the callback for the zeroth element if no initialValue
// is provided--it becomes the initial value and we move on to the next element.
accumulator = elem;
}
else {
// Normal path, invoking callback
accumulator = callbackfn(accumulator, elem, k, this);
}
k++;
}
if (k === 0 && !haveInitialValue) {
throw new TypeError('Reduce of empty TimeWindow with no initial value');
}
else {
return accumulator;
}
}
/**
* The some() method tests whether at least one element in the TimeWindow passes the test
* implemented by the provided function. If the window is empty then the method will always return false.
* @param {predicateCallback} callbackfn - Function to test for each element.
* @param {any} [thisArg] - Optional. Value to use as this when executing the callback.
* @returns {boolean} true if the callback function returns a truthy value for any array element; otherwise, false.
*/
some(callbackfn, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
let k = 0;
for (const elem of this) {
if (callbackfn.call(T, elem, k, this)) {
return true;
}
k++;
}
return false;
}
/**
* The every() method tests whether all elements in the TimeWindow pass the test implemented
* by the provided function. If the window is empty then the method will always return true.
* @param {predicateCallback} callbackfn - Function to test for each element.
* @param {any} [thisArg] - Optional. Value to use as this when executing the callback.
* @returns {boolean} true if the callback function returns a truthy value for every TimeWindow element; otherwise, false.
*/
every(callbackfn, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
let k = 0;
for (const elem of this) {
if (!callbackfn.call(T, elem, k, this)) {
return false;
}
k++;
}
return true;
}
/**
* Returns the value of the first element in the TimeWindow that satisfies the provided testing function. Otherwise undefined is returned.
* @param {predicateCallback} callbackfn - Function to test for each element.
* @param {any} [thisArg] - Optional. Value to use as this when executing the callback.
* @returns {any} The value of the first element in the TimeWindow that satisfies the provided testing function; otherwise, undefined is returned.
*/
find(callbackfn, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
let k = 0;
for (const elem of this) {
if (callbackfn.call(T, elem, k, this)) {
return elem;
}
k++;
}
return undefined;
}
/**
* Function that is executed on each element in a TimeWindow, taking three arguments:
*
* @callback forEachCallback
* @param {any} currentValue - The current element in the TimeWindow being processed.
* @param {number} [index] - The index of the current element being processed in the array.
* @param {TimeWindow} [window] - The TimeWindow that forEach() was called upon.
*/
/**
* Executes a provided function once for each TimeWindow element.
* @param {predicateCallback} callbackfn - Function to execute for each element.
* @param {any} [thisArg] - Optional. Value to use as this when executing the callback.
* @returns {undefined}
*/
forEach(callbackfn, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
let T = undefined;
// If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg != null) {
T = thisArg;
}
let k = 0;
for (const elem of this) {
callbackfn.call(T, elem, k, this);
k++;
}
}
}
module.exports = TimeWindow;