Skip to content

Commit

Permalink
tuning
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 9, 2018
1 parent 82b4542 commit 3d2aa11
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 44 deletions.
3 changes: 3 additions & 0 deletions src/internal/interable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

export interface IForEachAble<T> extends Iterable<T> {
forEach(callback: (v: T, i: number) => void): void;
}

export function isForEachAble<T>(v: IForEachAble<T> | any): v is IForEachAble<T> {
return typeof v.forEach === 'function';
}

export interface ISequence<T> extends IForEachAble<T> {
Expand Down
29 changes: 25 additions & 4 deletions src/internal/math.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ICategory} from '../model';
import {ICategory, isCategory} from '../model';
import {ISequence, IForEachAble} from './interable';
import {bisectLeft} from 'd3-array';

Expand Down Expand Up @@ -350,7 +350,7 @@ function computeGranularity(min: Date | null, max: Date | null) {
return {hist, histGranularity: EDateHistogramGranularity.MONTH};
}

export function computeDateStats(arr: ISequence<IForEachAble<Date | null>>, template?: IDateStatistics): IDateStatistics {
export function computeDateStats(arr: ISequence<IForEachAble<Date | null> | Date | null>, template?: IDateStatistics): IDateStatistics {
// filter out NaN
let min: Date | null = null;
let max: Date | null = null;
Expand All @@ -361,10 +361,22 @@ export function computeDateStats(arr: ISequence<IForEachAble<Date | null>>, temp
const byDay = new Map<number, number>();
arr.forEach((vs) => {
count += 1;
if (!vs) {
if (!vs || vs == null) {
missing += 1;
return;
}
if (vs instanceof Date) {
if (min == null || vs < min) {
min = vs;
}
if (max == null || vs > max) {
max = vs;
}
const key = vs.getFullYear() * 10000 + vs.getMonth() * 100 + vs.getDate();
byDay.set(key, (byDay.get(key) || 0) + 1);
return;
}

vs.forEach((v) => {
if (!v) {
missing += 1;
Expand Down Expand Up @@ -403,7 +415,7 @@ export function computeDateStats(arr: ISequence<IForEachAble<Date | null>>, temp
* @returns {{hist: {cat: string, y: number}[]}}
* @internal
*/
export function computeHist(arr: ISequence<IForEachAble<ICategory | null>>, categories: ICategory[]): ICategoricalStatistics {
export function computeHist(arr: ISequence<IForEachAble<ICategory | null> | ICategory | null>, categories: ICategory[]): ICategoricalStatistics {
const m = new Map<string, number>();
categories.forEach((cat) => m.set(cat.name, 0));

Expand All @@ -416,6 +428,15 @@ export function computeHist(arr: ISequence<IForEachAble<ICategory | null>>, cate
count += 1;
return;
}
if (isCategory(vs)) {
count += 1;
if (vs == null) {
missing += 1;
} else {
m.set(vs.name, (m.get(vs.name) || 0) + 1);
}
return;
}
vs.forEach((v) => {
count += 1;
if (v == null) {
Expand Down
4 changes: 4 additions & 0 deletions src/model/ICategoricalColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export interface ICategory {
value: number;
}

export function isCategory(v: any): v is ICategory {
return typeof v.name === 'string' && typeof v.label === 'string' && typeof v.color === 'string' && typeof v.value === 'number';
}

/** @internal */
export function toCategory(cat: (string | Partial<ICategory>), value: number, nextColor: () => string = () => Column.DEFAULT_COLOR) {
if (typeof cat === 'string') {
Expand Down
15 changes: 9 additions & 6 deletions src/provider/LocalDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export default class LocalDataProvider extends ACommonDataProvider {
}

private createSorter(ranking: Ranking, filter: ((d: IDataRow) => boolean) | null, isSortedBy: boolean) {
// not required if: sort criteria changed, group sort criteria changed
const groups = new Map<string, ISortHelper>();
const lookups = isSortedBy ? new CompareLookup(this._data.length, ranking.toCompareValueType()) : undefined;
let maxDataIndex = -1;
Expand Down Expand Up @@ -225,7 +224,6 @@ export default class LocalDataProvider extends ACommonDataProvider {
}

private sortGroup(g: ISortHelper, i: number, ranking: Ranking, lookups: CompareLookup | undefined, groupLookup: CompareLookup | undefined, singleGroup: boolean): Promise<IOrderedGroup> {
// not required if: group sort criteria changed
const group = g.group;

const sortTask = this.sortWorker.sort(g.rows, singleGroup, lookups);
Expand All @@ -242,8 +240,6 @@ export default class LocalDataProvider extends ACommonDataProvider {
}

private sortGroups(groups: IOrderedGroup[], groupLookup: CompareLookup | undefined) {
// not required if: sort criteria changed

// sort groups
if (!groupLookup) {
groups.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
Expand All @@ -269,32 +265,37 @@ export default class LocalDataProvider extends ACommonDataProvider {
}

sort(ranking: Ranking, _dirtyReason?: EDirtyReason) {
// clear summary not required if: sort criteria changed, group sort criteria changed, group criteria changed
// clear groups not required if: sort criteria changed, group sort criteria changed
// TODO clear summary not required if: sort criteria changed, group sort criteria changed, group criteria changed
// TODO clear groups not required if: sort criteria changed, group sort criteria changed
this.tasks.dirtyRanking(ranking, 'summary');

if (this._data.length === 0) {
return {groups: [], index2pos: []};
}

const filter = this.resolveFilter(ranking);
// TODO if no filter is set copy the data stats to the summary stats

const isGroupedBy = ranking.getGroupCriteria().length > 0;
const isSortedBy = ranking.getSortCriteria().length > 0;
const isGroupedSortedBy = ranking.getGroupSortCriteria().length > 0;

if (!isGroupedBy && !isSortedBy && !filter) {
// TODO copy data stats to summary and group stats
return this.noSorting();
}

// TODO not required if: sort criteria changed, group sort criteria changed
const {maxDataIndex, lookups, groups} = this.createSorter(ranking, filter, isSortedBy);

if (groups.size === 0) {
return {groups: [], index2pos: []};
}

if (groups.size === 1) {
// TODO can copy the summary stats to the group stats since the same
const g = Array.from(groups.values())[0]!;
// TODO not required if: group sort criteria changed
return this.sortGroup(g, 0, ranking, lookups, undefined, true).then((group) => {
this.tasks.dirtyRanking(ranking, 'summary'); // clean again
return this.index2pos([group], maxDataIndex);
Expand All @@ -304,8 +305,10 @@ export default class LocalDataProvider extends ACommonDataProvider {
const groupLookup = isGroupedSortedBy ? new CompareLookup(groups.size, ranking.toGroupCompareValueType()) : undefined;

return Promise.all(Array.from(groups.values()).map((g, i) => {
// TODO not required if: group sort criteria changed
return this.sortGroup(g, i, ranking, lookups, groupLookup, false);
})).then((groups) => {
// TODO not required if: sort criteria changed
const sortedGroups = this.sortGroups(groups, groupLookup);
this.tasks.dirtyRanking(ranking, 'summary'); // clean again
return this.index2pos(sortedGroups, maxDataIndex);
Expand Down
Loading

0 comments on commit 3d2aa11

Please sign in to comment.