UNPKG

jora

Version:

JavaScript object query engine

123 lines (96 loc) 3.81 kB
'use strict'; const heap = require('./heap.cjs'); const processNumericArray = require('./process-numeric-array.cjs'); function numbersPercentile(array, k, compare) { if (array.length === 0 || !isFinite(k) || k < 0 || k > 100) { return undefined; } const rank = k * (array.length - 1) / 100; // Apply a devision by 100 last to reduce a numerical error const lowerRank = Math.floor(rank); const upperRank = Math.ceil(rank); const heap$1 = k < 50 ? new heap.MinHeap(upperRank + 1, compare) : new heap.MaxHeap(array.length - lowerRank, compare); // (array.length - 1) - (lowerRank - 1) // heap.values = array.slice(0, heap.maxSize); // for (let i = 1; i < heap.maxSize; i++) { // heap.heapifyUp(i); // } for (let i = 0; i < array.length; i++) { const element = array[i]; if (Number.isNaN(element)) { return NaN; } heap$1.add(element); } if (lowerRank !== upperRank) { const a = heap$1.extract(); const b = heap$1.values[0]; // Given that both MinHeap and MaxHeap are utilized, the order of values could be either // ascending or descending. The following expression consistently uses the smaller value // as the base for the result. This approach helps to minimize numerical error. return a <= b ? a + (b - a) * (rank - lowerRank) : b + (a - b) * (rank - lowerRank); } return heap$1.values[0]; } function numbersMedian(array, compare) { return percentile(array, 50, compare); } function percentile(array, k, getter, compare) { if (array.length === 0 || !isFinite(k) || k < 0 || k > 100) { return undefined; } let arrayLength = 0; let rank = k * (array.length - 1) / 100; // Apply a devision by 100 last to reduce a numerical error let lowerRank = Math.floor(rank); let upperRank = Math.ceil(rank); let hasNaNs = false; const heap$1 = k < 50 ? new heap.MinHeap(upperRank + 1, compare) : new heap.MaxHeap(array.length - lowerRank, compare); // (array.length - 1) - (lowerRank - 1) processNumericArray.processNumericArray(array, getter, value => { if (Number.isNaN(value)) { hasNaNs = true; } heap$1.add(value); arrayLength++; }); if (hasNaNs) { return NaN; } // Adjust heap size and ranks when not all the values were accepted if (array.length !== arrayLength) { if (arrayLength === 0) { return; } rank = k * (arrayLength - 1) / 100; // Apply a devision by 100 last to reduce a numerical error lowerRank = Math.floor(rank); upperRank = Math.ceil(rank); const maxSize = k < 50 ? upperRank + 1 : arrayLength - lowerRank; for (let i = heap$1.values.length; i > maxSize; i--) { heap$1.extract(); } } if (lowerRank !== upperRank) { const a = heap$1.extract(); const b = heap$1.values[0]; // Given that both MinHeap and MaxHeap are utilized, the order of values could be either // ascending or descending. The following expression consistently uses the smaller value // as the base for the result. This approach helps to minimize numerical error. return a <= b ? a + (b - a) * (rank - lowerRank) : b + (a - b) * (rank - lowerRank); } return heap$1.values[0]; } function median(array, getter) { return percentile(array, 50, getter); } // console.log(percentile([1, 9, 4, 3, 2, 5, 6, 7, 8, 0, 10], 99.5)); exports.median = median; exports.numbersMedian = numbersMedian; exports.numbersPercentile = numbersPercentile; exports.percentile = percentile;