public
Created

  • Download Gist
table.js
JavaScript
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
// Table.js is a small library for manipulating NxN arrays.
// It takes an NxN array and a list of type formatters by column headers so:
// var table = new Table([['one', 'two', 'three'], ["1","jeff larson","3px"]], {
// one : function(it) { return +it; },
// two : function(it) { return it.toUpperCase(); },
// three : function(it) { return +it.replace("px", ''); }
// });
//
var Table = function(data, types){
this.data = data;
var defaults = _.reduce(this.headers(), function(memo, it){
memo[it] = function(data){ return data; };
return memo;
}, {});
_.extend(defaults, (types || {}));
this.types = defaults;
};
 
// Return the headers of the Table.
Table.prototype.headers = function(){
return this.data[0];
};
 
// Return the length of the data
Table.prototype.length = function(){
return this.data.length - 1;
};
 
// Get the raw data at idx
Table.prototype.getAt = function(idx){
var ret = {};
 
for(var i = 0; i < this.headers().length; i++)
ret[this.headers()[i]] = this.data[idx+1][i];
 
return ret;
};
 
// Get an object of "header" : "value" pairs for idx
Table.prototype.valuesAt = function(i){
var row = this.getAt(i);
var iter = _.bind(function(memo, it) {
memo[it] = this.types[it](row[it]);
return memo;
}, this);
return _.reduce(_.keys(row), iter, {});
};
 
// Simple each function, yeilds each value in the array.
Table.prototype.each = function(cb){
for(var i = 0; i < this.data.length - 1; i++) cb.call(this, this.valuesAt(i), i);
};
 
// Apply a function to each value and return an array of the results.
// Returns an array for simplicity's/deadline's sake.
Table.prototype.map = function(cb){
var ret = [];
 
this.each(function() {
var value = cb.apply(this, arguments);
ret.push(value);
});
 
return ret;
};
 
// Reduce the table to a single value.
Table.prototype.reduce = function(cb, initial){
var memo = initial;
 
this.each(function(){
var args = [].slice.call(arguments);
memo = cb.apply(this, [memo].concat(args));
});
 
return memo;
};
 
// Sort the array by a comparator and return a new Table.
Table.prototype.sortBy = function(cb){
var simpleArray = this.map(function(it){
return _.map(this.headers(), function(h) {
return it[h];
});
});
 
var arr = _.sortBy(simpleArray, cb);
 
return new Table([this.headers()].concat(arr), this.types);
};
 
// Always fun times writing these.
 
// Min value in a column.
Table.prototype.min = function(col) {
return this.reduce(function(memo, it){ return _.isNumber(it[col]) && it[col] < memo ? it[col] : memo; }, Infinity);
};
 
// Max value in a column.
Table.prototype.max = function(col){
return this.reduce(function(memo, it){ return _.isNumber(it[col]) && it[col] > memo ? it[col] : memo; }, -Infinity);
};
 
// Average value in a column.
Table.prototype.average = function(col){
return this.sum(col) / this.numLength(col)
};
 
// Length of the column with non-number values removed.
Table.prototype.numLength = function(col){
return this.reduce(function(memo, it) { return _.isNumber(it[col]) ? memo + 1 : memo; }, 0);
};
 
// Sum of a column
Table.prototype.sum = function(col){
return this.reduce(function(memo, it) { return _.isNumber(it[col]) ? memo + it[col] : memo }, 0);
};
 
// Variance of the values in a column
Table.prototype.variance = function(col) {
var average = this.average(col);
return this.reduce(function(memo, it) { return _.isNumber(it[col]) ? memo + Math.pow(it[col] - average, 2) : memo }, 0);
};
 
// Standard Deviation of the values in a column.
Table.prototype.stdev = function(col) {
return Math.sqrt(this.variance(col) / this.numLength(col))
};
 
// Pluck out columns from the table, e.g.:
// new Table([["one", "two", "three"],[1,2,3]].pluck("one", "two", "one plus two", function(arr, values) {
// arr.push(values.one + values.two); return arr;
// });
Table.prototype.pluck = function(/* keys, cb */){
var keys = [].slice.call(arguments);
var cb = _.isFunction(_.last(keys)) ? keys.pop() : function(it) { return it; };
var ret = this.map(function(values, idx){
var arr = [];
for(var i = 0; i < keys.length; i++)
if(values[keys[i]]) arr.push(values[keys[i]]);
return cb.call(this, arr, values, idx);
});
 
ret.unshift(keys);
 
return new Table(ret, this.types);
};
 
// Add a row from "key" : "value" pairs.
Table.prototype.addRow = function(row){
this.data.push(_.map(this.headers(), function(key){ return row[key]; }));
};
 
// Group the table into an object by headers, e.g.:
// var table = new Table([["one", "two", "three"],[2, 2, 3], [2, 5, 6], [3, 8, 9]])
// var facets =table.facet("one");
// facets[2]
// >>> [['one','two','three'],[2,2,3],[2,5,6]]
// facets[3]
// >>> [['one','two','three'],[3,8,9]]
Table.prototype.facet = function(facet_key){
return this.reduce(function(memo, values){
var table = memo[values[facet_key]] = (memo[values[facet_key]] || new Table([this.headers()], this.types));
table.addRow(values);
return memo;
}, {});
};

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.