Skip to content

Instantly share code, notes, and snippets.

@thejefflarson
Created March 1, 2012 17:53
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thejefflarson/1951671 to your computer and use it in GitHub Desktop.
Save thejefflarson/1951671 to your computer and use it in GitHub Desktop.
// 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;
}, {});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment