- init
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Built-in functions: a plain table { NAME -> fn(values) }.
|
||||
*
|
||||
* By the time a function runs, the evaluator has already:
|
||||
* - resolved every range argument into a flat list of cell values,
|
||||
* - flattened scalar arguments into that same list,
|
||||
* - checked for errors among the values (an error short-circuits and is
|
||||
* returned instead of calling the function).
|
||||
*
|
||||
* So a function only ever sees `(number | string | null)[]`. Each function
|
||||
* decides for itself how to treat text and empty cells. Per spec:
|
||||
* - empty cell -> ignored by every aggregate, never counted by COUNT
|
||||
* - text -> ignored by the numeric aggregates
|
||||
*/
|
||||
|
||||
import { FormulaError, type CellValue } from './types';
|
||||
|
||||
export type FunctionFn = (values: CellValue[]) => CellValue;
|
||||
|
||||
/** Keep only real numbers (finite). */
|
||||
function nums(values: CellValue[]): number[] {
|
||||
const out: number[] = [];
|
||||
for (const v of values) {
|
||||
if (typeof v === 'number' && Number.isFinite(v)) out.push(v);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export const FUNCTIONS: Record<string, FunctionFn> = {
|
||||
SUM(values) {
|
||||
return nums(values).reduce((a, b) => a + b, 0);
|
||||
},
|
||||
|
||||
AVG(values) {
|
||||
const n = nums(values);
|
||||
if (n.length === 0) return new FormulaError('#DIV/0!', 'AVG of no numbers');
|
||||
return n.reduce((a, b) => a + b, 0) / n.length;
|
||||
},
|
||||
|
||||
MIN(values) {
|
||||
const n = nums(values);
|
||||
return n.length ? Math.min(...n) : 0;
|
||||
},
|
||||
|
||||
MAX(values) {
|
||||
const n = nums(values);
|
||||
return n.length ? Math.max(...n) : 0;
|
||||
},
|
||||
|
||||
COUNT(values) {
|
||||
return nums(values).length;
|
||||
},
|
||||
};
|
||||
|
||||
// AVERAGE is an alias of AVG.
|
||||
FUNCTIONS.AVERAGE = FUNCTIONS.AVG;
|
||||
|
||||
export function hasFunction(name: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(FUNCTIONS, name.toUpperCase());
|
||||
}
|
||||
|
||||
export function getFunction(name: string): FunctionFn | undefined {
|
||||
return FUNCTIONS[name.toUpperCase()];
|
||||
}
|
||||
Reference in New Issue
Block a user