src/range.js
/**
* @external {Range} https://github.com/Reading-eScience-Centre/coverage-jsapi/blob/master/Range.md
*/
/**
* Return the minimum/maximum across all range values, ignoring null's.
*
* @param {Range<number>} range The numeric coverage data range.
* @return {[min,max]} The minimum and maximum values of the range,
* or [undefined, undefined] if the range contains only `null` values.
*/
export function minMaxOfRange (range) {
let min = Infinity
let max = -Infinity
let fn = val => {
if (val === null) return
if (val < min) min = val
if (val > max) max = val
}
iterateRange(range, fn)
return min === Infinity ? [undefined, undefined] : [min, max]
}
/**
* Apply a reduce function over the range values.
*
* @param {Range} range The coverage data range.
* @param {function} callback Function to execute on each value in the array with arguments `(previousValue, currentValue)`.
* @param start Value to use as the first argument to the first call of the `callback`.
* @return The reduced value.
*/
export function reduceRange (range, callback, start) {
let v1 = start
let iterFn = v2 => {
v1 = callback(v1, v2)
}
iterateRange(range, iterFn)
return v1
}
/**
* Iterate over all range values and run a function for each value.
* No particular iteration order must be assumed.
*/
export function iterateRange (range, fn) {
// We use a precompiled function here for efficiency.
// See below for a slower recursive version.
// Benchmarks compared to recursive version:
// Chrome 46: around 1.03x faster
// Firefox 42: around 2x faster (and around 6x faster than Chrome 46!)
// nest loops from smallest to biggest
let shape = [...range.shape]
shape.sort(([, size1], [, size2]) => size1 - size2)
let begin = 'var obj = {}'
let end = ''
for (let [axis, size] of shape) {
begin += `
for (var i${axis}=0; i${axis} < ${size}; ++i${axis}) {
obj['${axis}'] = i${axis}
`
end += `}`
}
begin += `
fn(get(obj))
`
let iterateLoop = new Function(`return function iterRange (get, fn) { ${begin} ${end} }`)() // eslint-disable-line
iterateLoop(range.get, fn)
}
/*
* Recursive version of iterate(). For reference only.
*
export function iterate (range, fn) {
let get = range.get
let shape = [...range.shape]
// iterate from smallest to biggest dimension
shape.sort(([,size1], [,size2]) => size1 - size2)
let dims = shape.length
function iterateRecurse (obj, axisIdx) {
if (dims === axisIdx) {
fn(get(obj))
} else {
let [axis,size] = shape[axisIdx]
for (let i=0; i < size; i++) {
obj[axis] = i
iterateRecurse(obj, axisIdx+1)
}
}
}
iterateRecurse({}, 0)
}
*/