Skip to content

Instantly share code, notes, and snippets.

@afruzan
Last active April 17, 2023 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afruzan/a3b1f224726e7bf1aef010e189be761b to your computer and use it in GitHub Desktop.
Save afruzan/a3b1f224726e7bf1aef010e189be761b to your computer and use it in GitHub Desktop.
Convert flat array of items to Pivot Table data in JavaScript
// convert flat array of items to pivot table data
// uses linq.js
import Enumerable from 'linq';
export interface Column {
columnName: string;
sourceField: string;
sourceValue?: string;
isTotal?: boolean;
isKey?: boolean;
}
export function aggregate_sum(value: any, currentValue: any, groupCount: number) {
if (!value) {
return currentValue;
}
return Number(value) + Number(currentValue ?? 0);
}
export function aggregate_avg(value: any, currentValue: any, groupCount: number) {
if (!value) {
return currentValue;
}
return (Number(value) / groupCount) + Number(currentValue ?? 0);
}
export function defaultColumnName(field: string, value: any, isTotal: boolean) {
return isTotal ? (field + ':' + 'total') : (field + ':' + (value ?? 'null').toString());
}
export function pivotize(
data: {}[],
keyFields: string[],
columnFields: string[],
valueField: string,
addTotalColumns: boolean = true,
addTotalRow: boolean = true,
aggregate: (value: any, currentValue: any, groupCount: number) => number = aggregate_sum,
getColumnName: (field: string, value: any, isTotal: boolean) => string = defaultColumnName)
: { table: {}[]; columns: Column[] } {
let columns: Column[] = [];
let columns_names = [];
const addColumn = (column: Column) => {
if (!columns_names.includes(column.columnName)) {
columns_names.push(column.columnName);
columns.push(column);
}
};
for (let field of keyFields) {
addColumn({ columnName: field, sourceField: field, isKey: true });
}
let table = Enumerable.from(data)
.groupBy(
item => {
var key = {};
for (let field of keyFields) {
key[field] = item[field];
}
return key;
},
i => i,
(key, items) => {
var row = {};
for (let field of keyFields) {
row[field] = key[field];
}
for (let col_field of columnFields) {
const col_target_field_total = addTotalColumns ? getColumnName(col_field, undefined, true) : null;
for (let item of items) {
const col_value = item[col_field];
const value = item[valueField];
const col_target_field = getColumnName(col_field, col_value, false);
if (col_target_field) {
addColumn({ columnName: col_target_field, sourceField: col_field, sourceValue: col_value });
row[col_target_field] = aggregate(value, row[col_target_field], items.count());
}
if (col_target_field_total) {
addColumn({ columnName: col_target_field_total, sourceField: col_field, isTotal: true });
row[col_target_field_total] = aggregate(value, row[col_target_field_total], items.count());
}
}
}
return row;
},
key => JSON.stringify(key))
.toArray();
if (addTotalRow) {
var totalRow = { isTotalRow: true };
for (let col of columns) {
if (!col.isKey) {
totalRow[col.columnName] = table.reduce((total, item) => ({ [col.columnName]: aggregate(item[col.columnName], total[col.columnName], table.length) }))[col.columnName];
}
}
table.push(totalRow);
}
return { table: table, columns: columns };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment