Home Reference Source Repository

src/array.js

export function minMax (arr) {
  var len = arr.length
  var min = Infinity
  var max = -Infinity
  while (len--) {
    var el = arr[len]
    if (el == null) {
      // do nothing
    } else if (el < min) {
      min = el
    } else if (el > max) {
      max = el
    }
  }
  if (min === Infinity) {
    min = max
  } else if (max === -Infinity) {
    max = min
  }
  if (min === Infinity || min === -Infinity) {
    // all values were null
    min = null
    max = null
  }
  return [min, max]
}

/**
 * Return the indices of the two neighbors in the sorted array closest to the given number.
 *
 * @example
 * var a = [2,5,8,12,13]
 * var i = CovUtils.indicesOfNearest(a, 6)
 * // i == [1,2]
 * var j = CovUtils.indicesOfNearest(a, 5)
 * // j == [1,1]
 * var k = CovUtils.indicesOfNearest(a, 50)
 * // k == [4,4]
 *
 * @param {Array<number>} a The array to search through. Must be sorted, ascending or descending.
 * @param {number} x The target number.
 * @return {[lo,hi]} The indices of the two closest values, may be equal.
 *   If `x` exists in the array, both neighbors point to `x`.
 *   If `x` is lower (greater if descending) than the first value, both neighbors point to 0.
 *   If `x` is greater (lower if descending) than the last value, both neighbors point to the last index.
 */
export function indicesOfNearest (a, x) {
  if (a.length === 0) {
    throw new Error('Array must have at least one element')
  }
  var lo = -1
  var hi = a.length
  const ascending = a.length === 1 || a[0] < a[1]
  // we have two separate code paths to help the runtime optimize the loop
  if (ascending) {
    while (hi - lo > 1) {
      let mid = Math.round((lo + hi) / 2)
      if (a[mid] <= x) {
        lo = mid
      } else {
        hi = mid
      }
    }
  } else {
    while (hi - lo > 1) {
      let mid = Math.round((lo + hi) / 2)
      if (a[mid] >= x) { // here's the difference
        lo = mid
      } else {
        hi = mid
      }
    }
  }
  if (a[lo] === x) hi = lo
  if (lo === -1) lo = hi
  if (hi === a.length) hi = lo
  return [lo, hi]
}

/**
 * Return the index of the value closest to the given number in a sorted array.
 *
 * @example
 * var a = [2,5,8,12,13]
 * var i = CovUtils.indexOfNearest(a, 6)
 * // i == 1
 * var j = CovUtils.indexOfNearest(a, 7)
 * // j == 2
 * var k = CovUtils.indexOfNearest(a, 50)
 * // k == 4
 *
 * @param {Array<number>} a The array to search through. Must be sorted, ascending or descending.
 * @param {number} x The target number.
 * @return {number} The array index whose value is closest to `x`.
 *   If `x` happens to be exactly between two values, then the lower index is returned.
 */
export function indexOfNearest (a, x) {
  var i = indicesOfNearest(a, x)
  var lo = i[0]
  var hi = i[1]
  if (Math.abs(x - a[lo]) <= Math.abs(x - a[hi])) {
    return lo
  } else {
    return hi
  }
}