@antv/g2
Version:
the Grammar of Graphics in Javascript
127 lines (111 loc) • 3.48 kB
text/typescript
import { group, ascending, maxIndex, Primitive } from 'd3-array';
import { defined } from '../../utils/helper';
import { TabularData, G2Mark } from '../../runtime';
import { indexOf } from '../../utils/array';
import { columnOf } from './helper';
export type IndexComparatorFactory = (
data: TabularData,
Y: Primitive[],
S: Primitive[],
) => IndexComparator;
export type IndexComparator = (i: number, j: number) => number;
export type Order =
| 'value'
| 'sum'
| 'series'
| 'maxIndex'
| string[]
| ((data: Record<string, Primitive>) => Primitive);
export function createGroups(
groupBy: string | string[],
I: number[],
mark: G2Mark,
): number[][] {
const { encode } = mark;
if (groupBy === null) return [I];
const G = normalizeGroupBy(groupBy)
.map((k) => [k, columnOf(encode, k)?.[0]] as const)
.filter(([, column]) => defined(column));
const key = (i: number) => G.map(([, V]) => V[i]).join('-');
return Array.from(group(I, key).values());
}
export function normalizeComparator(
order: Order,
): IndexComparatorFactory | null {
if (Array.isArray(order)) return createFieldsOrder(order);
if (typeof order === 'function') return createFunctionOrder(order);
if (order === 'series') return createSeriesOrder;
if (order === 'value') return createValueOrder;
if (order === 'sum') return createSumOrder;
if (order === 'maxIndex') return createMaxIndexOrder;
return null;
}
export function applyOrder(groups: number[][], comparator: IndexComparator) {
for (const group of groups) {
group.sort(comparator);
}
}
export function domainOf(value: Primitive[], scale: Record<string, any>) {
return scale?.domain || Array.from(new Set(value));
}
function normalizeGroupBy(groupBy: string | string[]): string[] {
if (Array.isArray(groupBy)) return groupBy;
return [groupBy];
}
function createSeriesOrder(
data: TabularData,
Y: Primitive[],
S: Primitive[],
): IndexComparator {
return ascendingComparator((i: number) => S[i]);
}
function createFunctionOrder(
order: (data: Record<string, Primitive>) => Primitive,
): IndexComparatorFactory {
return (data, Y, S) => {
return ascendingComparator((i) => order(data[i]));
};
}
function createFieldsOrder(order: string[]): IndexComparatorFactory {
return (data, Y, S) => {
return (i, j) =>
order.reduce(
(eq, f) => (eq !== 0 ? eq : ascending(data[i][f], data[j][f])),
0,
);
};
}
function createValueOrder(
data: TabularData,
Y: Primitive[],
S: Primitive[],
): IndexComparator {
return ascendingComparator((i: number) => Y[i]);
}
function createSumOrder(
data: TabularData,
Y: Primitive[],
S: Primitive[],
): IndexComparator {
const I = indexOf(data);
const groups = Array.from(group(I, (i) => S[+i]).entries());
const seriesSum = new Map(
groups.map(([k, GI]) => [k, GI.reduce((s, i) => s + +Y[i])] as const),
);
return ascendingComparator((i: number) => seriesSum.get(S[i]));
}
function createMaxIndexOrder(
data: TabularData,
Y: Primitive[],
S: Primitive[],
): IndexComparator {
const I = indexOf(data);
const groups = Array.from(group(I, (i) => S[+i]).entries());
const seriesMaxIndex = new Map(
groups.map(([k, GI]) => [k, maxIndex(GI, (i) => Y[i])] as const),
);
return ascendingComparator((i: number) => seriesMaxIndex.get(S[i]));
}
function ascendingComparator(order: (i: number) => any): IndexComparator {
return (i, j) => ascending(order(i), order(j));
}