Last active
April 17, 2023 18:08
-
-
Save afruzan/a3b1f224726e7bf1aef010e189be761b to your computer and use it in GitHub Desktop.
Convert flat array of items to Pivot Table data in JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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