Created
March 1, 2012 17:53
-
-
Save thejefflarson/1951671 to your computer and use it in GitHub Desktop.
This file contains 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
// 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