Skip to content

Commit 6c2b7f2

Browse files
committed
New feature - Add utility functions
1 parent 1f5965b commit 6c2b7f2

File tree

4 files changed

+220
-6
lines changed

4 files changed

+220
-6
lines changed

src/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineAsyncComponent } from "vue";
22
import getVueDataUiConfig from "./getVueDataUiConfig";
33
import getThemeConfig from "./getThemeConfig";
4-
import { getPalette, createWordCloudDatasetFromPlainText, abbreviate, createTSpans, createStraightPath, createSmoothPath } from "./lib";
4+
import { getPalette, createWordCloudDatasetFromPlainText, abbreviate, createTSpans, createStraightPath, createSmoothPath, getCumulativeAverage, getCumulativeMedian } from "./lib";
55
import { lightenColor, darkenColor, shiftColorHue } from "./exposedLib";
66

77
export const Arrow = defineAsyncComponent(() => import("./atoms/Arrow.vue"))
@@ -72,14 +72,16 @@ export const VueUiRidgeline = defineAsyncComponent(() => import('./components/vu
7272
export const VueUiChord = defineAsyncComponent(() => import('./components/vue-ui-chord.vue'))
7373
export {
7474
abbreviate,
75+
createSmoothPath,
76+
createStraightPath,
77+
createTSpans,
7578
createWordCloudDatasetFromPlainText,
7679
darkenColor,
80+
getCumulativeAverage,
81+
getCumulativeMedian,
7782
getPalette,
7883
getThemeConfig,
7984
getVueDataUiConfig,
8085
lightenColor,
8186
shiftColorHue,
82-
createTSpans,
83-
createStraightPath,
84-
createSmoothPath
8587
}

src/lib.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,68 @@ export function easeOutCubic(t) {
25102510
return 1 - Math.pow(1 - t, 3);
25112511
}
25122512

2513+
export function getCumulativeAverage({ values, config = {} }) {
2514+
const {
2515+
keepInvalid = true,
2516+
convertInvalidToZero = false,
2517+
} = config;
2518+
2519+
const avg = [];
2520+
let sum = 0;
2521+
let count = 0;
2522+
2523+
function isInvalid(n) {
2524+
return typeof n !== "number" || !Number.isFinite(n)
2525+
}
2526+
2527+
function addAvg(n) {
2528+
sum += n;
2529+
count += 1;
2530+
avg.push(sum / count);
2531+
}
2532+
2533+
for (const v of values) {
2534+
if(!isInvalid(v)) addAvg(v);
2535+
else if (convertInvalidToZero && keepInvalid) addAvg(0);
2536+
else if (!convertInvalidToZero && keepInvalid) avg.push(v);
2537+
}
2538+
2539+
return avg;
2540+
}
2541+
2542+
export function getCumulativeMedian({ values, config = {} }) {
2543+
const {
2544+
keepInvalid = true,
2545+
convertInvalidToZero = false,
2546+
} = config;
2547+
2548+
const medians = [];
2549+
const list = [];
2550+
2551+
function isInvalid(n) {
2552+
return typeof n !== "number" || !Number.isFinite(n);
2553+
}
2554+
2555+
function addMedian(n) {
2556+
list.push(n);
2557+
list.sort((a, b) => a - b);
2558+
const len = list.length;
2559+
const mid = len >> 1;
2560+
if (len % 2 === 1) {
2561+
medians.push(list[mid]);
2562+
} else {
2563+
medians.push((list[mid - 1] + list[mid]) / 2);
2564+
}
2565+
}
2566+
2567+
for (const v of values) {
2568+
if (!isInvalid(v)) addMedian(v)
2569+
else if (convertInvalidToZero && keepInvalid) addMedian(0);
2570+
else if (!convertInvalidToZero && keepInvalid) medians.push(v);
2571+
}
2572+
return medians;
2573+
}
2574+
25132575

25142576
const lib = {
25152577
XMLNS,
@@ -2561,6 +2623,8 @@ const lib = {
25612623
functionReturnsString,
25622624
generateSpiralCoordinates,
25632625
getCloserPoint,
2626+
getCumulativeAverage,
2627+
getCumulativeMedian,
25642628
getMissingDatasetAttributes,
25652629
getPalette,
25662630
getScaleFactorUsingArcSize,

tests/lib.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import {
4646
functionReturnsString,
4747
generateSpiralCoordinates,
4848
getCloserPoint,
49+
getCumulativeAverage,
50+
getCumulativeMedian,
4951
getMissingDatasetAttributes,
5052
getScaleFactorUsingArcSize,
5153
hasDeepProperty,
@@ -3441,4 +3443,56 @@ describe('createSmoothAreaSegments', () => {
34413443
expect(result.length).toBe(1);
34423444
expect(roundPathNumbers(result[0])).toBe(roundPathNumbers(seg));
34433445
});
3446+
});
3447+
3448+
describe('getCumulativeAverage', () => {
3449+
const valid = [0, 1, 2, 3, 1];
3450+
const invalid = [0, 1, NaN, undefined, null, Infinity, -Infinity, 2, 3, 1];
3451+
test('returns cumulative average for a complete array of numbers', () => {
3452+
expect(getCumulativeAverage({values: valid})).toEqual([0, 0.5, 1, 1.5, 1.4]);
3453+
});
3454+
3455+
test('returns cumulative average and invalid values, but invalid values ignored in average', () => {
3456+
expect(getCumulativeAverage({values: invalid})).toEqual([0, 0.5, NaN, undefined, null, Infinity, -Infinity, 1 , 1.5, 1.4]);
3457+
});
3458+
3459+
test('returns cumulative average without invalid values', () => {
3460+
expect(getCumulativeAverage({
3461+
values: invalid,
3462+
config: { keepInvalid: false }
3463+
})).toEqual([0, 0.5, 1, 1.5, 1.4])
3464+
});
3465+
3466+
test ('returns cumulative average with zero values replacing invalid values', () => {
3467+
expect(getCumulativeAverage({
3468+
values: invalid,
3469+
config: { convertInvalidToZero: true }
3470+
})).toEqual([0, 0.5, 0.3333333333333333, 0.25, 0.2, 0.16666666666666666, 0.14285714285714285, 0.375, 0.6666666666666666, 0.7])
3471+
})
3472+
});
3473+
3474+
describe('getCumulativeMedian', () => {
3475+
const valid = [0, 1, 2, 3, 1];
3476+
const invalid = [0, 1, NaN, undefined, null, Infinity, -Infinity, 2, 3, 1];
3477+
test('returns cumulative median for a complete array of numbers', () => {
3478+
expect(getCumulativeMedian({values: valid})).toEqual([0, 0.5, 1, 1.5, 1]);
3479+
});
3480+
3481+
test('returns cumulative median and invalid values, but invalid values ignored in median', () => {
3482+
expect(getCumulativeMedian({values: invalid})).toEqual([0, 0.5, NaN, undefined, null, Infinity, -Infinity, 1, 1.5, 1]);
3483+
});
3484+
3485+
test('returns cumulative median without invalid values', () => {
3486+
expect(getCumulativeMedian({
3487+
values: invalid,
3488+
config: { keepInvalid: false }
3489+
})).toEqual([0, 0.5, 1, 1.5, 1])
3490+
});
3491+
3492+
test ('returns cumulative median with zero values replacing invalid values', () => {
3493+
expect(getCumulativeMedian({
3494+
values: invalid,
3495+
config: { convertInvalidToZero: true }
3496+
})).toEqual([0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0])
3497+
})
34443498
});

types/vue-data-ui.d.ts

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6119,7 +6119,7 @@ declare module "vue-data-ui" {
61196119
verticalAlign?: string;
61206120
};
61216121
tr?: {
6122-
height?: number;
6122+
height?: number;
61236123
style?: {
61246124
backgroundColor?: string;
61256125
color?: string;
@@ -6967,7 +6967,7 @@ declare module "vue-data-ui" {
69676967
};
69686968
zeroLine?: {
69696969
show?: boolean;
6970-
strokeWidth?:number;
6970+
strokeWidth?: number;
69716971
strokeDasharray?: number;
69726972
useSerieColor?: boolean;
69736973
stroke?: string;
@@ -7224,6 +7224,100 @@ declare module "vue-data-ui" {
72247224
y: number
72257225
}
72267226

7227+
/**
7228+
* Configuration options for cumulative functions
7229+
*/
7230+
export type CumulativeConfig = {
7231+
/**
7232+
* If `true`, invalid inputs are kept (and echoed) in the output.
7233+
* Defaults to `true`.
7234+
*/
7235+
keepInvalid?: boolean;
7236+
/**
7237+
* If `true`, invalid inputs are treated as zero when computing the statistic.
7238+
* Defaults to `false`.
7239+
*/
7240+
convertInvalidToZero?: boolean;
7241+
}
7242+
7243+
/**
7244+
* Vue Data UI utility
7245+
* ---
7246+
* Compute the cumulative median of a sequence, optionally echoing or zero-filling invalid inputs.
7247+
* ---
7248+
* @example
7249+
* ```js
7250+
* // Simple usage
7251+
* const arr = [1, 2, 3, 4, 5];
7252+
* const medians = getCumulativeMedian({ values: arr });
7253+
*
7254+
* // Ignore invalid values entirely
7255+
* const arrWithInvalid = [1, null, 2, Infinity, undefined];
7256+
* const mediansNoInvalid = getCumulativeMedian({
7257+
* values: arrWithInvalid,
7258+
* config: { keepInvalid: false }
7259+
* });
7260+
*
7261+
* // Convert invalid values to zero
7262+
* const mediansZeroed = getCumulativeMedian({
7263+
* values: arrWithInvalid,
7264+
* config: { convertInvalidToZero: true }
7265+
* });
7266+
* ```
7267+
*
7268+
* @param {Object} params
7269+
* @param {Array<number|*>} params.values
7270+
* The input sequence. Can include numbers or any “invalid” placeholders.
7271+
* @param {CumulativeConfig} [params.config]
7272+
* Configuration flags to control handling of invalid inputs.
7273+
* @returns {Array<number|*>}
7274+
* An array where each slot is either the cumulative median up to that point,
7275+
* or the original invalid value if `keepInvalid` is `true`.
7276+
*/
7277+
export function getCumulativeMedian<T = unknown>(params: {
7278+
values: Array<number | T>;
7279+
config?: CumulativeConfig;
7280+
}): Array<number | T>;
7281+
7282+
/**
7283+
* Vue Data UI utility
7284+
* ---
7285+
* Compute the cumulative median of a sequence, optionally echoing or zero-filling invalid inputs.
7286+
* ---
7287+
* @example
7288+
* ```js
7289+
* // Simple usage
7290+
* const arr = [1, 2, 3, 4, 5];
7291+
* const medians = getCumulativeMedian({ values: arr });
7292+
*
7293+
* // Ignore invalid values entirely
7294+
* const arrWithInvalid = [1, null, 2, Infinity, undefined];
7295+
* const mediansNoInvalid = getCumulativeMedian({
7296+
* values: arrWithInvalid,
7297+
* config: { keepInvalid: false }
7298+
* });
7299+
*
7300+
* // Convert invalid values to zero
7301+
* const mediansZeroed = getCumulativeMedian({
7302+
* values: arrWithInvalid,
7303+
* config: { convertInvalidToZero: true }
7304+
* });
7305+
* ```
7306+
*
7307+
* @param {Object} params
7308+
* @param {Array<number|*>} params.values
7309+
* The input sequence. Can include numbers or any “invalid” placeholders.
7310+
* @param {CumulativeConfig} [params.config]
7311+
* Configuration flags to control handling of invalid inputs.
7312+
* @returns {Array<number|*>}
7313+
* An array where each slot is either the cumulative median up to that point,
7314+
* or the original invalid value if `keepInvalid` is `true`.
7315+
*/
7316+
export function getCumulativeAverage<T = unknown>(params: {
7317+
values: Array<number | T>;
7318+
config?: CumulativeConfig;
7319+
}): Array<number | T>;
7320+
72277321
/**
72287322
* Vue Data UI utility
72297323
* ---

0 commit comments

Comments
 (0)