jora
Version:
JavaScript object query engine
123 lines (96 loc) • 3.81 kB
JavaScript
;
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;