|
@@ -3,7 +3,7 @@ import isNumber from 'lodash/isNumber';
|
|
|
|
|
|
|
|
import { SeriesData, NullValueMode } from '../types/index';
|
|
import { SeriesData, NullValueMode } from '../types/index';
|
|
|
|
|
|
|
|
-export enum StatID {
|
|
|
|
|
|
|
+export enum ReducerID {
|
|
|
sum = 'sum',
|
|
sum = 'sum',
|
|
|
max = 'max',
|
|
max = 'max',
|
|
|
min = 'min',
|
|
min = 'min',
|
|
@@ -24,14 +24,14 @@ export enum StatID {
|
|
|
allIsNull = 'allIsNull',
|
|
allIsNull = 'allIsNull',
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export interface FieldStats {
|
|
|
|
|
|
|
+export interface FieldCalcs {
|
|
|
[key: string]: any;
|
|
[key: string]: any;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Internal function
|
|
// Internal function
|
|
|
-type StatCalculator = (data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => FieldStats;
|
|
|
|
|
|
|
+type FieldReducer = (data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => FieldCalcs;
|
|
|
|
|
|
|
|
-export interface StatCalculatorInfo {
|
|
|
|
|
|
|
+export interface FieldReducerInfo {
|
|
|
id: string;
|
|
id: string;
|
|
|
name: string;
|
|
name: string;
|
|
|
description: string;
|
|
description: string;
|
|
@@ -40,16 +40,16 @@ export interface StatCalculatorInfo {
|
|
|
// Internal details
|
|
// Internal details
|
|
|
emptyInputResult?: any; // typically null, but some things like 'count' & 'sum' should be zero
|
|
emptyInputResult?: any; // typically null, but some things like 'count' & 'sum' should be zero
|
|
|
standard: boolean; // The most common stats can all be calculated in a single pass
|
|
standard: boolean; // The most common stats can all be calculated in a single pass
|
|
|
- calculator?: StatCalculator;
|
|
|
|
|
|
|
+ reduce?: FieldReducer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* @param ids list of stat names or null to get all of them
|
|
* @param ids list of stat names or null to get all of them
|
|
|
*/
|
|
*/
|
|
|
-export function getStatsCalculators(ids?: string[]): StatCalculatorInfo[] {
|
|
|
|
|
|
|
+export function getFieldReducers(ids?: string[]): FieldReducerInfo[] {
|
|
|
if (ids === null || ids === undefined) {
|
|
if (ids === null || ids === undefined) {
|
|
|
if (!hasBuiltIndex) {
|
|
if (!hasBuiltIndex) {
|
|
|
- getById(StatID.mean);
|
|
|
|
|
|
|
+ getById(ReducerID.mean);
|
|
|
}
|
|
}
|
|
|
return listOfStats;
|
|
return listOfStats;
|
|
|
}
|
|
}
|
|
@@ -60,53 +60,53 @@ export function getStatsCalculators(ids?: string[]): StatCalculatorInfo[] {
|
|
|
list.push(stat);
|
|
list.push(stat);
|
|
|
}
|
|
}
|
|
|
return list;
|
|
return list;
|
|
|
- }, new Array<StatCalculatorInfo>());
|
|
|
|
|
|
|
+ }, new Array<FieldReducerInfo>());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export interface CalculateStatsOptions {
|
|
|
|
|
|
|
+interface ReduceFieldOptions {
|
|
|
series: SeriesData;
|
|
series: SeriesData;
|
|
|
fieldIndex: number;
|
|
fieldIndex: number;
|
|
|
- stats: string[]; // The stats to calculate
|
|
|
|
|
|
|
+ reducers: string[]; // The stats to calculate
|
|
|
nullValueMode?: NullValueMode;
|
|
nullValueMode?: NullValueMode;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* @returns an object with a key for each selected stat
|
|
* @returns an object with a key for each selected stat
|
|
|
*/
|
|
*/
|
|
|
-export function calculateStats(options: CalculateStatsOptions): FieldStats {
|
|
|
|
|
- const { series, fieldIndex, stats, nullValueMode } = options;
|
|
|
|
|
|
|
+export function reduceField(options: ReduceFieldOptions): FieldCalcs {
|
|
|
|
|
+ const { series, fieldIndex, reducers, nullValueMode } = options;
|
|
|
|
|
|
|
|
- if (!stats || stats.length < 1) {
|
|
|
|
|
|
|
+ if (!reducers || reducers.length < 1) {
|
|
|
return {};
|
|
return {};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const queue = getStatsCalculators(stats);
|
|
|
|
|
|
|
+ const queue = getFieldReducers(reducers);
|
|
|
|
|
|
|
|
// Return early for empty series
|
|
// Return early for empty series
|
|
|
// This lets the concrete implementations assume at least one row
|
|
// This lets the concrete implementations assume at least one row
|
|
|
if (!series.rows || series.rows.length < 1) {
|
|
if (!series.rows || series.rows.length < 1) {
|
|
|
- const stats = {} as FieldStats;
|
|
|
|
|
- for (const stat of queue) {
|
|
|
|
|
- stats[stat.id] = stat.emptyInputResult !== null ? stat.emptyInputResult : null;
|
|
|
|
|
|
|
+ const calcs = {} as FieldCalcs;
|
|
|
|
|
+ for (const reducer of queue) {
|
|
|
|
|
+ calcs[reducer.id] = reducer.emptyInputResult !== null ? reducer.emptyInputResult : null;
|
|
|
}
|
|
}
|
|
|
- return stats;
|
|
|
|
|
|
|
+ return calcs;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const ignoreNulls = nullValueMode === NullValueMode.Ignore;
|
|
const ignoreNulls = nullValueMode === NullValueMode.Ignore;
|
|
|
const nullAsZero = nullValueMode === NullValueMode.AsZero;
|
|
const nullAsZero = nullValueMode === NullValueMode.AsZero;
|
|
|
|
|
|
|
|
// Avoid calculating all the standard stats if possible
|
|
// Avoid calculating all the standard stats if possible
|
|
|
- if (queue.length === 1 && queue[0].calculator) {
|
|
|
|
|
- return queue[0].calculator(series, fieldIndex, ignoreNulls, nullAsZero);
|
|
|
|
|
|
|
+ if (queue.length === 1 && queue[0].reduce) {
|
|
|
|
|
+ return queue[0].reduce(series, fieldIndex, ignoreNulls, nullAsZero);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// For now everything can use the standard stats
|
|
// For now everything can use the standard stats
|
|
|
- let values = standardStatsStat(series, fieldIndex, ignoreNulls, nullAsZero);
|
|
|
|
|
- for (const calc of queue) {
|
|
|
|
|
- if (!values.hasOwnProperty(calc.id) && calc.calculator) {
|
|
|
|
|
|
|
+ let values = doStandardCalcs(series, fieldIndex, ignoreNulls, nullAsZero);
|
|
|
|
|
+ for (const reducer of queue) {
|
|
|
|
|
+ if (!values.hasOwnProperty(reducer.id) && reducer.reduce) {
|
|
|
values = {
|
|
values = {
|
|
|
...values,
|
|
...values,
|
|
|
- ...calc.calculator(series, fieldIndex, ignoreNulls, nullAsZero),
|
|
|
|
|
|
|
+ ...reducer.reduce(series, fieldIndex, ignoreNulls, nullAsZero),
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -121,30 +121,30 @@ export function calculateStats(options: CalculateStatsOptions): FieldStats {
|
|
|
|
|
|
|
|
// private registry of all stats
|
|
// private registry of all stats
|
|
|
interface TableStatIndex {
|
|
interface TableStatIndex {
|
|
|
- [id: string]: StatCalculatorInfo;
|
|
|
|
|
|
|
+ [id: string]: FieldReducerInfo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const listOfStats: StatCalculatorInfo[] = [];
|
|
|
|
|
|
|
+const listOfStats: FieldReducerInfo[] = [];
|
|
|
const index: TableStatIndex = {};
|
|
const index: TableStatIndex = {};
|
|
|
let hasBuiltIndex = false;
|
|
let hasBuiltIndex = false;
|
|
|
|
|
|
|
|
-function getById(id: string): StatCalculatorInfo | undefined {
|
|
|
|
|
|
|
+function getById(id: string): FieldReducerInfo | undefined {
|
|
|
if (!hasBuiltIndex) {
|
|
if (!hasBuiltIndex) {
|
|
|
[
|
|
[
|
|
|
{
|
|
{
|
|
|
- id: StatID.last,
|
|
|
|
|
|
|
+ id: ReducerID.last,
|
|
|
name: 'Last',
|
|
name: 'Last',
|
|
|
description: 'Last Value (current)',
|
|
description: 'Last Value (current)',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
alias: 'current',
|
|
alias: 'current',
|
|
|
- calculator: calculateLast,
|
|
|
|
|
|
|
+ reduce: calculateLast,
|
|
|
},
|
|
},
|
|
|
- { id: StatID.first, name: 'First', description: 'First Value', standard: true, calculator: calculateFirst },
|
|
|
|
|
- { id: StatID.min, name: 'Min', description: 'Minimum Value', standard: true },
|
|
|
|
|
- { id: StatID.max, name: 'Max', description: 'Maximum Value', standard: true },
|
|
|
|
|
- { id: StatID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
|
|
|
|
|
|
+ { id: ReducerID.first, name: 'First', description: 'First Value', standard: true, reduce: calculateFirst },
|
|
|
|
|
+ { id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
|
|
|
|
|
+ { id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
|
|
|
|
|
+ { id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
|
|
{
|
|
{
|
|
|
- id: StatID.sum,
|
|
|
|
|
|
|
+ id: ReducerID.sum,
|
|
|
name: 'Total',
|
|
name: 'Total',
|
|
|
description: 'The sum of all values',
|
|
description: 'The sum of all values',
|
|
|
emptyInputResult: 0,
|
|
emptyInputResult: 0,
|
|
@@ -152,55 +152,55 @@ function getById(id: string): StatCalculatorInfo | undefined {
|
|
|
alias: 'total',
|
|
alias: 'total',
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.count,
|
|
|
|
|
|
|
+ id: ReducerID.count,
|
|
|
name: 'Count',
|
|
name: 'Count',
|
|
|
description: 'Number of values in response',
|
|
description: 'Number of values in response',
|
|
|
emptyInputResult: 0,
|
|
emptyInputResult: 0,
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.range,
|
|
|
|
|
|
|
+ id: ReducerID.range,
|
|
|
name: 'Range',
|
|
name: 'Range',
|
|
|
description: 'Difference between minimum and maximum values',
|
|
description: 'Difference between minimum and maximum values',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.delta,
|
|
|
|
|
|
|
+ id: ReducerID.delta,
|
|
|
name: 'Delta',
|
|
name: 'Delta',
|
|
|
description: 'Cumulative change in value',
|
|
description: 'Cumulative change in value',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.step,
|
|
|
|
|
|
|
+ id: ReducerID.step,
|
|
|
name: 'Step',
|
|
name: 'Step',
|
|
|
description: 'Minimum interval between values',
|
|
description: 'Minimum interval between values',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.diff,
|
|
|
|
|
|
|
+ id: ReducerID.diff,
|
|
|
name: 'Difference',
|
|
name: 'Difference',
|
|
|
description: 'Difference between first and last values',
|
|
description: 'Difference between first and last values',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.logmin,
|
|
|
|
|
|
|
+ id: ReducerID.logmin,
|
|
|
name: 'Min (above zero)',
|
|
name: 'Min (above zero)',
|
|
|
description: 'Used for log min scale',
|
|
description: 'Used for log min scale',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.changeCount,
|
|
|
|
|
|
|
+ id: ReducerID.changeCount,
|
|
|
name: 'Change Count',
|
|
name: 'Change Count',
|
|
|
description: 'Number of times the value changes',
|
|
description: 'Number of times the value changes',
|
|
|
standard: false,
|
|
standard: false,
|
|
|
- calculator: calculateChangeCount,
|
|
|
|
|
|
|
+ reduce: calculateChangeCount,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- id: StatID.distinctCount,
|
|
|
|
|
|
|
+ id: ReducerID.distinctCount,
|
|
|
name: 'Distinct Count',
|
|
name: 'Distinct Count',
|
|
|
description: 'Number of distinct values',
|
|
description: 'Number of distinct values',
|
|
|
standard: false,
|
|
standard: false,
|
|
|
- calculator: calculateDistinctCount,
|
|
|
|
|
|
|
+ reduce: calculateDistinctCount,
|
|
|
},
|
|
},
|
|
|
].forEach(info => {
|
|
].forEach(info => {
|
|
|
const { id, alias } = info;
|
|
const { id, alias } = info;
|
|
@@ -222,13 +222,8 @@ function getById(id: string): StatCalculatorInfo | undefined {
|
|
|
return index[id];
|
|
return index[id];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function standardStatsStat(
|
|
|
|
|
- data: SeriesData,
|
|
|
|
|
- fieldIndex: number,
|
|
|
|
|
- ignoreNulls: boolean,
|
|
|
|
|
- nullAsZero: boolean
|
|
|
|
|
-): FieldStats {
|
|
|
|
|
- const stats = {
|
|
|
|
|
|
|
+function doStandardCalcs(data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
|
|
|
|
|
+ const calcs = {
|
|
|
sum: 0,
|
|
sum: 0,
|
|
|
max: -Number.MAX_VALUE,
|
|
max: -Number.MAX_VALUE,
|
|
|
min: Number.MAX_VALUE,
|
|
min: Number.MAX_VALUE,
|
|
@@ -247,7 +242,7 @@ function standardStatsStat(
|
|
|
|
|
|
|
|
// Just used for calcutations -- not exposed as a stat
|
|
// Just used for calcutations -- not exposed as a stat
|
|
|
previousDeltaUp: true,
|
|
previousDeltaUp: true,
|
|
|
- } as FieldStats;
|
|
|
|
|
|
|
+ } as FieldCalcs;
|
|
|
|
|
|
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
|
let currentValue = data.rows[i][fieldIndex];
|
|
let currentValue = data.rows[i][fieldIndex];
|
|
@@ -262,94 +257,94 @@ function standardStatsStat(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (currentValue !== null) {
|
|
if (currentValue !== null) {
|
|
|
- const isFirst = stats.first === null;
|
|
|
|
|
|
|
+ const isFirst = calcs.first === null;
|
|
|
if (isFirst) {
|
|
if (isFirst) {
|
|
|
- stats.first = currentValue;
|
|
|
|
|
|
|
+ calcs.first = currentValue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (isNumber(currentValue)) {
|
|
if (isNumber(currentValue)) {
|
|
|
- stats.sum += currentValue;
|
|
|
|
|
- stats.allIsNull = false;
|
|
|
|
|
- stats.nonNullCount++;
|
|
|
|
|
|
|
+ calcs.sum += currentValue;
|
|
|
|
|
+ calcs.allIsNull = false;
|
|
|
|
|
+ calcs.nonNullCount++;
|
|
|
|
|
|
|
|
if (!isFirst) {
|
|
if (!isFirst) {
|
|
|
- const step = currentValue - stats.last!;
|
|
|
|
|
- if (stats.step > step) {
|
|
|
|
|
- stats.step = step; // the minimum interval
|
|
|
|
|
|
|
+ const step = currentValue - calcs.last!;
|
|
|
|
|
+ if (calcs.step > step) {
|
|
|
|
|
+ calcs.step = step; // the minimum interval
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.last! > currentValue) {
|
|
|
|
|
|
|
+ if (calcs.last! > currentValue) {
|
|
|
// counter reset
|
|
// counter reset
|
|
|
- stats.previousDeltaUp = false;
|
|
|
|
|
|
|
+ calcs.previousDeltaUp = false;
|
|
|
if (i === data.rows.length - 1) {
|
|
if (i === data.rows.length - 1) {
|
|
|
// reset on last
|
|
// reset on last
|
|
|
- stats.delta += currentValue;
|
|
|
|
|
|
|
+ calcs.delta += currentValue;
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- if (stats.previousDeltaUp) {
|
|
|
|
|
- stats.delta += step; // normal increment
|
|
|
|
|
|
|
+ if (calcs.previousDeltaUp) {
|
|
|
|
|
+ calcs.delta += step; // normal increment
|
|
|
} else {
|
|
} else {
|
|
|
- stats.delta += currentValue; // account for counter reset
|
|
|
|
|
|
|
+ calcs.delta += currentValue; // account for counter reset
|
|
|
}
|
|
}
|
|
|
- stats.previousDeltaUp = true;
|
|
|
|
|
|
|
+ calcs.previousDeltaUp = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (currentValue > stats.max) {
|
|
|
|
|
- stats.max = currentValue;
|
|
|
|
|
|
|
+ if (currentValue > calcs.max) {
|
|
|
|
|
+ calcs.max = currentValue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (currentValue < stats.min) {
|
|
|
|
|
- stats.min = currentValue;
|
|
|
|
|
|
|
+ if (currentValue < calcs.min) {
|
|
|
|
|
+ calcs.min = currentValue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (currentValue < stats.logmin && currentValue > 0) {
|
|
|
|
|
- stats.logmin = currentValue;
|
|
|
|
|
|
|
+ if (currentValue < calcs.logmin && currentValue > 0) {
|
|
|
|
|
+ calcs.logmin = currentValue;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (currentValue !== 0) {
|
|
if (currentValue !== 0) {
|
|
|
- stats.allIsZero = false;
|
|
|
|
|
|
|
+ calcs.allIsZero = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- stats.last = currentValue;
|
|
|
|
|
|
|
+ calcs.last = currentValue;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.max === -Number.MAX_VALUE) {
|
|
|
|
|
- stats.max = null;
|
|
|
|
|
|
|
+ if (calcs.max === -Number.MAX_VALUE) {
|
|
|
|
|
+ calcs.max = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.min === Number.MAX_VALUE) {
|
|
|
|
|
- stats.min = null;
|
|
|
|
|
|
|
+ if (calcs.min === Number.MAX_VALUE) {
|
|
|
|
|
+ calcs.min = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.step === Number.MAX_VALUE) {
|
|
|
|
|
- stats.step = null;
|
|
|
|
|
|
|
+ if (calcs.step === Number.MAX_VALUE) {
|
|
|
|
|
+ calcs.step = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.nonNullCount > 0) {
|
|
|
|
|
- stats.mean = stats.sum! / stats.nonNullCount;
|
|
|
|
|
|
|
+ if (calcs.nonNullCount > 0) {
|
|
|
|
|
+ calcs.mean = calcs.sum! / calcs.nonNullCount;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.max !== null && stats.min !== null) {
|
|
|
|
|
- stats.range = stats.max - stats.min;
|
|
|
|
|
|
|
+ if (calcs.max !== null && calcs.min !== null) {
|
|
|
|
|
+ calcs.range = calcs.max - calcs.min;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (stats.first !== null && stats.last !== null) {
|
|
|
|
|
- if (isNumber(stats.first) && isNumber(stats.last)) {
|
|
|
|
|
- stats.diff = stats.last - stats.first;
|
|
|
|
|
|
|
+ if (calcs.first !== null && calcs.last !== null) {
|
|
|
|
|
+ if (isNumber(calcs.first) && isNumber(calcs.last)) {
|
|
|
|
|
+ calcs.diff = calcs.last - calcs.first;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return stats;
|
|
|
|
|
|
|
+ return calcs;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function calculateFirst(data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldStats {
|
|
|
|
|
|
|
+function calculateFirst(data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
|
|
|
return { first: data.rows[0][fieldIndex] };
|
|
return { first: data.rows[0][fieldIndex] };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function calculateLast(data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldStats {
|
|
|
|
|
|
|
+function calculateLast(data: SeriesData, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
|
|
|
return { last: data.rows[data.rows.length - 1][fieldIndex] };
|
|
return { last: data.rows[data.rows.length - 1][fieldIndex] };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -358,7 +353,7 @@ function calculateChangeCount(
|
|
|
fieldIndex: number,
|
|
fieldIndex: number,
|
|
|
ignoreNulls: boolean,
|
|
ignoreNulls: boolean,
|
|
|
nullAsZero: boolean
|
|
nullAsZero: boolean
|
|
|
-): FieldStats {
|
|
|
|
|
|
|
+): FieldCalcs {
|
|
|
let count = 0;
|
|
let count = 0;
|
|
|
let first = true;
|
|
let first = true;
|
|
|
let last: any = null;
|
|
let last: any = null;
|
|
@@ -387,7 +382,7 @@ function calculateDistinctCount(
|
|
|
fieldIndex: number,
|
|
fieldIndex: number,
|
|
|
ignoreNulls: boolean,
|
|
ignoreNulls: boolean,
|
|
|
nullAsZero: boolean
|
|
nullAsZero: boolean
|
|
|
-): FieldStats {
|
|
|
|
|
|
|
+): FieldCalcs {
|
|
|
const distinct = new Set<any>();
|
|
const distinct = new Set<any>();
|
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
|
let currentValue = data.rows[i][fieldIndex];
|
|
let currentValue = data.rows[i][fieldIndex];
|