locutus
Version:
Locutus other languages' standard libraries to JavaScript for fun and educational purposes
80 lines (79 loc) • 3.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.median = median;
function median(data) {
// discuss at: https://locutus.io/python/statistics/median/
// parity verified: Python 3.12
// original by: Kevin van Zonneveld (https://kvz.io)
// note 1: Returns the middle data point, averaging the two middle numeric points for even-length input.
// example 1: median([1, 3, 5])
// returns 1: 3
// example 2: median([1, 2, 3, 4])
// returns 2: 2.5
// example 3: median(['b', 'a', 'c'])
// returns 3: 'b'
const values = sortStatisticsValues(data, 'median');
if (values.length === 0) {
throw new Error('no median for empty data');
}
const middleIndex = Math.floor(values.length / 2);
if (values.length % 2 === 1) {
return values[middleIndex];
}
const left = values[middleIndex - 1];
const right = values[middleIndex];
if (typeof left === 'string' || typeof right === 'string') {
throw new TypeError("unsupported operand type(s) for /: 'str' and 'int'");
}
return (toStatisticNumber(left, 'median') + toStatisticNumber(right, 'median')) / 2;
}
function sortStatisticsValues(data, functionName) {
if (!Array.isArray(data)) {
throw new TypeError(`${functionName}() data must be an array`);
}
if (data.length === 0) {
return [];
}
const values = data;
const firstKind = getStatisticsSortKind(values[0], functionName);
for (let index = 1; index < values.length; index += 1) {
if (getStatisticsSortKind(values[index], functionName) !== firstKind) {
throw new TypeError(`${functionName}() requires data values that can be ordered together`);
}
}
return [...values].sort((left, right) => compareStatisticsValues(left, right, firstKind));
}
function toStatisticNumber(value, functionName) {
if (typeof value === 'number') {
return value;
}
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
if (typeof value === 'bigint') {
const numericValue = Number(value);
if (!Number.isSafeInteger(numericValue)) {
throw new RangeError(`${functionName}() bigint values must fit within JS safe integers`);
}
return numericValue;
}
throw new TypeError(`${functionName}() data must contain only real numbers`);
}
function getStatisticsSortKind(value, functionName) {
if (typeof value === 'string') {
return 'string';
}
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
return 'number';
}
throw new TypeError(`${functionName}() requires sortable scalar values`);
}
function compareStatisticsValues(left, right, kind) {
if (kind === 'string') {
return String(left).localeCompare(String(right));
}
return toNumericSortValue(left) - toNumericSortValue(right);
}
function toNumericSortValue(value) {
return typeof value === 'boolean' ? (value ? 1 : 0) : Number(value);
}