react-pivot
Version:
React-Pivot is a data-grid component with pivot-table-like functionality for data display, filtering, and exploration.
184 lines (140 loc) • 4.32 kB
JavaScript
var _ = require('lodash')
module.exports = function(opts) {return new DataFrame(opts)}
function DataFrame (opts) {
this.rows = opts.rows
this.dimensions = opts.dimensions
this.reduce = opts.reduce
this.cache = {}
return this
}
DataFrame.prototype.calculate = function(opts) {
this.activeDimensions = opts.dimensions
if (this.activeDimensions.length < 1) this.activeDimensions = ['']
this.sortBy = opts.sortBy
this.sortDir = opts.sortDir
this.filter = opts.filter
this.compact = opts.compact
var results = this.getResults()
var resultRows = this.parseResults(results)
return resultRows
}
DataFrame.prototype.getResults = function() {
var self = this
var columns = this.getColumns()
var activeDimensions = this.activeDimensions
var filter = this.filter
var reduce = this.reduce
var results = {}
var setKeyCache = {}
this.rows.forEach(function(row) {
var setKeys = self.createSetKeys(activeDimensions, row)
var dVals = parseSetKey(setKeys[setKeys.length-1])
if (filter && !filter(dVals)) return
var curLevel = results
setKeys.forEach(function(setKey, iLevel) {
if (!curLevel[setKey]) {
curLevel[setKey] = {value: {}, subDimensions: {}, key: setKey}
}
var result = curLevel[setKey].value
if (filter || !self.cache[setKey]) {
if (!filter) setKeyCache[setKey] = result
_.extend(result, reduce(row, result))
var dimensionVals = parseSetKey(setKey)
_.extend(result, dimensionVals)
} else {
curLevel[setKey].value = self.cache[setKey]
}
curLevel = curLevel[setKey].subDimensions
})
})
_.each(setKeyCache, function(cache, key) {
self.cache[key] = cache
})
return results
}
DataFrame.prototype.parseResults = function(results, level) {
var self = this
var level = level || 0
var rows = []
var sorted = _.sortBy(results, this.getSortValue.bind(this))
if (this.sortDir === 'desc') sorted.reverse()
_.each(sorted, function(dimension) {
var total = dimension.value
total._level = level
total._key = dimension.key
var numSubDimensions = Object.keys(dimension.subDimensions).length;
if(self.compact && (numSubDimensions == 1)) {
// don't push the row
} else {
rows.push(total)
}
if (numSubDimensions) {
var subLevel = (self.compact && numSubDimensions == 1) ? level : level + 1;
var subRows = self.parseResults(dimension.subDimensions, subLevel)
subRows.forEach(function(subRow) {rows.push(subRow)})
}
})
return rows
}
DataFrame.prototype.getColumns = function() {
var columns = []
this.dimensions.forEach(function(d) {
columns.push({type: 'dimension', title: d, value: d})
})
return columns
}
DataFrame.prototype.createSetKeys = function(dimensions, row) {
var keys = []
for (var i = 0; i < dimensions.length; i++) {
var sds = dimensions.slice(0, i+1)
keys.push(this.createSetKey(sds, row))
}
return keys
}
DataFrame.prototype.createSetKey = function (dimensions, row) {
var self = this
var key = ''
_.sortBy(dimensions).forEach(function(dTitle) {
var dimension = self.findDimension(dTitle)
key += [dTitle, getValue(dimension, row)].join('\xff') + '\xff'
})
return key
}
DataFrame.prototype.findDimension = function (title) {
return _.find(this.dimensions, function(d) {
return d.title === title
})
}
DataFrame.prototype.getSortValue = function(result) {
var sortBy = this.sortBy
var columns = this.getColumns()
var sortCol = _.find(columns, function(c) {
return c.title === sortBy
}) || sortBy
var val = getValue(sortCol, result.value)
if (typeof val === 'undefined') return result.key
return val
}
function parseSetKey (setKey) {
var parsed = {}
var kvPairs = setKey.split('\xff')
for (var i = 0; i < kvPairs.length; i += 2) {
var dTitle = kvPairs[i]
var dVal = kvPairs[i+1]
if (dTitle) parsed[dTitle] = dVal
}
return parsed
}
function getValue (col, row) {
if (col == null) return null
if (typeof col === 'string') {
var val = row[col]
} else if (typeof col === 'function') {
var val = col(row)
} else if (typeof col.value === 'string') {
var val = row[col.value]
} else {
var val = col.value(row)
}
return val
}