/** * @module ol/array */ /** * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1. * https://github.com/darkskyapp/binary-search * * @param {Array<*>} haystack Items to search through. * @param {*} needle The item to look for. * @param {Function} [comparator] Comparator function. * @return {number} The index of the item if found, -1 if not. */ export function binarySearch(haystack, needle, comparator) { let mid, cmp; comparator = comparator || ascending; let low = 0; let high = haystack.length; let found = false; while (low < high) { /* Note that "(low + high) >>> 1" may overflow, and results in a typecast * to double (which gives the wrong results). */ mid = low + ((high - low) >> 1); cmp = +comparator(haystack[mid], needle); if (cmp < 0.0) { /* Too low. */ low = mid + 1; } else { /* Key found or too high */ high = mid; found = !cmp; } } /* Key not found. */ return found ? low : ~low; } /** * Compare function sorting arrays in ascending order. Safe to use for numeric values. * @param {*} a The first object to be compared. * @param {*} b The second object to be compared. * @return {number} A negative number, zero, or a positive number as the first * argument is less than, equal to, or greater than the second. */ export function ascending(a, b) { return a > b ? 1 : a < b ? -1 : 0; } /** * Compare function sorting arrays in descending order. Safe to use for numeric values. * @param {*} a The first object to be compared. * @param {*} b The second object to be compared. * @return {number} A negative number, zero, or a positive number as the first * argument is greater than, equal to, or less than the second. */ export function descending(a, b) { return a < b ? 1 : a > b ? -1 : 0; } /** * {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution} can use a function * of this type to determine which nearest resolution to use. * * This function takes a `{number}` representing a value between two array entries, * a `{number}` representing the value of the nearest higher entry and * a `{number}` representing the value of the nearest lower entry * as arguments and returns a `{number}`. If a negative number or zero is returned * the lower value will be used, if a positive number is returned the higher value * will be used. * @typedef {function(number, number, number): number} NearestDirectionFunction * @api */ /** * @param {Array} arr Array in descending order. * @param {number} target Target. * @param {number|NearestDirectionFunction} direction * 0 means return the nearest, * > 0 means return the largest nearest, * < 0 means return the smallest nearest. * @return {number} Index. */ export function linearFindNearest(arr, target, direction) { if (arr[0] <= target) { return 0; } const n = arr.length; if (target <= arr[n - 1]) { return n - 1; } if (typeof direction === 'function') { for (let i = 1; i < n; ++i) { const candidate = arr[i]; if (candidate === target) { return i; } if (candidate < target) { if (direction(target, arr[i - 1], candidate) > 0) { return i - 1; } return i; } } return n - 1; } if (direction > 0) { for (let i = 1; i < n; ++i) { if (arr[i] < target) { return i - 1; } } return n - 1; } if (direction < 0) { for (let i = 1; i < n; ++i) { if (arr[i] <= target) { return i; } } return n - 1; } for (let i = 1; i < n; ++i) { if (arr[i] == target) { return i; } if (arr[i] < target) { if (arr[i - 1] - target < target - arr[i]) { return i - 1; } return i; } } return n - 1; } /** * @param {Array<*>} arr Array. * @param {number} begin Begin index. * @param {number} end End index. */ export function reverseSubArray(arr, begin, end) { while (begin < end) { const tmp = arr[begin]; arr[begin] = arr[end]; arr[end] = tmp; ++begin; --end; } } /** * @param {Array} arr The array to modify. * @param {!Array|VALUE} data The elements or arrays of elements to add to arr. * @template VALUE */ export function extend(arr, data) { const extension = Array.isArray(data) ? data : [data]; const length = extension.length; for (let i = 0; i < length; i++) { arr[arr.length] = extension[i]; } } /** * @param {Array} arr The array to modify. * @param {VALUE} obj The element to remove. * @template VALUE * @return {boolean} If the element was removed. */ export function remove(arr, obj) { const i = arr.indexOf(obj); const found = i > -1; if (found) { arr.splice(i, 1); } return found; } /** * @param {Array|Uint8ClampedArray} arr1 The first array to compare. * @param {Array|Uint8ClampedArray} arr2 The second array to compare. * @return {boolean} Whether the two arrays are equal. */ export function equals(arr1, arr2) { const len1 = arr1.length; if (len1 !== arr2.length) { return false; } for (let i = 0; i < len1; i++) { if (arr1[i] !== arr2[i]) { return false; } } return true; } /** * Sort the passed array such that the relative order of equal elements is preserved. * See https://en.wikipedia.org/wiki/Sorting_algorithm#Stability for details. * @param {Array<*>} arr The array to sort (modifies original). * @param {!function(*, *): number} compareFnc Comparison function. * @api */ export function stableSort(arr, compareFnc) { const length = arr.length; const tmp = Array(arr.length); let i; for (i = 0; i < length; i++) { tmp[i] = {index: i, value: arr[i]}; } tmp.sort(function (a, b) { return compareFnc(a.value, b.value) || a.index - b.index; }); for (i = 0; i < arr.length; i++) { arr[i] = tmp[i].value; } } /** * @param {Array<*>} arr The array to test. * @param {Function} [func] Comparison function. * @param {boolean} [strict] Strictly sorted (default false). * @return {boolean} Return index. */ export function isSorted(arr, func, strict) { const compare = func || ascending; return arr.every(function (currentVal, index) { if (index === 0) { return true; } const res = compare(arr[index - 1], currentVal); return !(res > 0 || (strict && res === 0)); }); }