1 | /**
|
2 | * @external {Range} https://github.com/Reading-eScience-Centre/coverage-jsapi/blob/master/Range.md
|
3 | */
|
4 |
|
5 | /**
|
6 | * Return the minimum/maximum across all range values, ignoring null's.
|
7 | *
|
8 | * @param {Range<number>} range The numeric coverage data range.
|
9 | * @return {[min,max]} The minimum and maximum values of the range,
|
10 | * or [undefined, undefined] if the range contains only `null` values.
|
11 | */
|
12 | export function minMaxOfRange (range) {
|
13 | let min = Infinity
|
14 | let max = -Infinity
|
15 | let fn = val => {
|
16 | if (val === null) return
|
17 | if (val < min) min = val
|
18 | if (val > max) max = val
|
19 | }
|
20 | iterateRange(range, fn)
|
21 | return min === Infinity ? [undefined, undefined] : [min, max]
|
22 | }
|
23 |
|
24 | /**
|
25 | * Apply a reduce function over the range values.
|
26 | *
|
27 | * @param {Range} range The coverage data range.
|
28 | * @param {function} callback Function to execute on each value in the array with arguments `(previousValue, currentValue)`.
|
29 | * @param start Value to use as the first argument to the first call of the `callback`.
|
30 | * @return The reduced value.
|
31 | */
|
32 | export function reduceRange (range, callback, start) {
|
33 | let v1 = start
|
34 | let iterFn = v2 => {
|
35 | v1 = callback(v1, v2)
|
36 | }
|
37 | iterateRange(range, iterFn)
|
38 | return v1
|
39 | }
|
40 |
|
41 | /**
|
42 | * Iterate over all range values and run a function for each value.
|
43 | * No particular iteration order must be assumed.
|
44 | */
|
45 | export function iterateRange (range, fn) {
|
46 | // We use a precompiled function here for efficiency.
|
47 | // See below for a slower recursive version.
|
48 |
|
49 | // Benchmarks compared to recursive version:
|
50 | // Chrome 46: around 1.03x faster
|
51 | // Firefox 42: around 2x faster (and around 6x faster than Chrome 46!)
|
52 |
|
53 | // nest loops from smallest to biggest
|
54 | let shape = [...range.shape]
|
55 | shape.sort(([, size1], [, size2]) => size1 - size2)
|
56 |
|
57 | let begin = 'var obj = {}'
|
58 | let end = ''
|
59 | for (let [axis, size] of shape) {
|
60 | begin += `
|
61 | for (var i${axis}=0; i${axis} < ${size}; ++i${axis}) {
|
62 | obj['${axis}'] = i${axis}
|
63 | `
|
64 | end += `}`
|
65 | }
|
66 | begin += `
|
67 | fn(get(obj))
|
68 | `
|
69 |
|
70 | let iterateLoop = new Function(`return function iterRange (get, fn) { ${begin} ${end} }`)() // eslint-disable-line
|
71 | iterateLoop(range.get, fn)
|
72 | }
|
73 |
|
74 | /*
|
75 | * Recursive version of iterate(). For reference only.
|
76 | *
|
77 | export function iterate (range, fn) {
|
78 | let get = range.get
|
79 | let shape = [...range.shape]
|
80 | // iterate from smallest to biggest dimension
|
81 | shape.sort(([,size1], [,size2]) => size1 - size2)
|
82 | let dims = shape.length
|
83 |
|
84 | function iterateRecurse (obj, axisIdx) {
|
85 | if (dims === axisIdx) {
|
86 | fn(get(obj))
|
87 | } else {
|
88 | let [axis,size] = shape[axisIdx]
|
89 | for (let i=0; i < size; i++) {
|
90 | obj[axis] = i
|
91 | iterateRecurse(obj, axisIdx+1)
|
92 | }
|
93 | }
|
94 | }
|
95 | iterateRecurse({}, 0)
|
96 | }
|
97 | */
|