forked from davidguttman/node-dataframe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
196 lines (151 loc) · 4.49 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
var _ = {
extend: require('lodash/extend'),
each: require('lodash/each'),
sortBy: require('lodash/sortBy'),
find: require('lodash/find')
}
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
if (!isNaN(parseFloat(val)) && isFinite(val)){
return +val
} else if (typeof(val) === 'string') {
return val.toLowerCase()
} else {
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
}