UNPKG

datalib

Version:

JavaScript utilites for loading, summarizing and working with data.

299 lines (240 loc) 7.36 kB
var u = module.exports; // utility functions var FNAME = '__name__'; u.namedfunc = function(name, f) { return (f[FNAME] = name, f); }; u.name = function(f) { return f==null ? null : f[FNAME]; }; u.identity = function(x) { return x; }; u.true = u.namedfunc('true', function() { return true; }); u.false = u.namedfunc('false', function() { return false; }); u.duplicate = function(obj) { return JSON.parse(JSON.stringify(obj)); }; u.equal = function(a, b) { return JSON.stringify(a) === JSON.stringify(b); }; u.extend = function(obj) { for (var x, name, i=1, len=arguments.length; i<len; ++i) { x = arguments[i]; for (name in x) { obj[name] = x[name]; } } return obj; }; u.length = function(x) { return x != null && x.length != null ? x.length : null; }; u.keys = function(x) { var keys = [], k; for (k in x) keys.push(k); return keys; }; u.vals = function(x) { var vals = [], k; for (k in x) vals.push(x[k]); return vals; }; u.toMap = function(list, f) { return (f = u.$(f)) ? list.reduce(function(obj, x) { return (obj[f(x)] = 1, obj); }, {}) : list.reduce(function(obj, x) { return (obj[x] = 1, obj); }, {}); }; u.keystr = function(values) { // use to ensure consistent key generation across modules var n = values.length; if (!n) return ''; for (var s=String(values[0]), i=1; i<n; ++i) { s += '|' + String(values[i]); } return s; }; // type checking functions var toString = Object.prototype.toString; u.isObject = function(obj) { return obj === Object(obj); }; u.isFunction = function(obj) { return toString.call(obj) === '[object Function]'; }; u.isString = function(obj) { return typeof value === 'string' || toString.call(obj) === '[object String]'; }; u.isArray = Array.isArray || function(obj) { return toString.call(obj) === '[object Array]'; }; u.isNumber = function(obj) { return typeof obj === 'number' || toString.call(obj) === '[object Number]'; }; u.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; }; u.isDate = function(obj) { return toString.call(obj) === '[object Date]'; }; u.isValid = function(obj) { return obj != null && obj === obj; }; u.isBuffer = (typeof Buffer === 'function' && Buffer.isBuffer) || u.false; // type coercion functions u.number = function(s) { return s == null || s === '' ? null : +s; }; u.boolean = function(s) { return s == null || s === '' ? null : s==='false' ? false : !!s; }; // parse a date with optional d3.time-format format u.date = function(s, format) { var d = format ? format : Date; return s == null || s === '' ? null : d.parse(s); }; u.array = function(x) { return x != null ? (u.isArray(x) ? x : [x]) : []; }; u.str = function(x) { return u.isArray(x) ? '[' + x.map(u.str) + ']' : u.isObject(x) || u.isString(x) ? // Output valid JSON and JS source strings. // See http://timelessrepo.com/json-isnt-a-javascript-subset JSON.stringify(x).replace('\u2028','\\u2028').replace('\u2029', '\\u2029') : x; }; // data access functions var field_re = /\[(.*?)\]|[^.\[]+/g; u.field = function(f) { return String(f).match(field_re).map(function(d) { return d[0] !== '[' ? d : d[1] !== "'" && d[1] !== '"' ? d.slice(1, -1) : d.slice(2, -2).replace(/\\(["'])/g, '$1'); }); }; u.accessor = function(f) { /* jshint evil: true */ return f==null || u.isFunction(f) ? f : u.namedfunc(f, Function('x', 'return x[' + u.field(f).map(u.str).join('][') + '];')); }; // short-cut for accessor u.$ = u.accessor; u.mutator = function(f) { var s; return u.isString(f) && (s=u.field(f)).length > 1 ? function(x, v) { for (var i=0; i<s.length-1; ++i) x = x[s[i]]; x[s[i]] = v; } : function(x, v) { x[f] = v; }; }; u.$func = function(name, op) { return function(f) { f = u.$(f) || u.identity; var n = name + (u.name(f) ? '_'+u.name(f) : ''); return u.namedfunc(n, function(d) { return op(f(d)); }); }; }; u.$valid = u.$func('valid', u.isValid); u.$length = u.$func('length', u.length); u.$in = function(f, values) { f = u.$(f); var map = u.isArray(values) ? u.toMap(values) : values; return function(d) { return !!map[f(d)]; }; }; // comparison / sorting functions u.comparator = function(sort) { var sign = []; if (sort === undefined) sort = []; sort = u.array(sort).map(function(f) { var s = 1; if (f[0] === '-') { s = -1; f = f.slice(1); } else if (f[0] === '+') { s = +1; f = f.slice(1); } sign.push(s); return u.accessor(f); }); return function(a, b) { var i, n, f, c; for (i=0, n=sort.length; i<n; ++i) { f = sort[i]; c = u.cmp(f(a), f(b)); if (c) return c * sign[i]; } return 0; }; }; u.cmp = function(a, b) { return (a < b || a == null) && b != null ? -1 : (a > b || b == null) && a != null ? 1 : ((b = b instanceof Date ? +b : b), (a = a instanceof Date ? +a : a)) !== a && b === b ? -1 : b !== b && a === a ? 1 : 0; }; u.numcmp = function(a, b) { return a - b; }; u.stablesort = function(array, sortBy, keyFn) { var indices = array.reduce(function(idx, v, i) { return (idx[keyFn(v)] = i, idx); }, {}); array.sort(function(a, b) { var sa = sortBy(a), sb = sortBy(b); return sa < sb ? -1 : sa > sb ? 1 : (indices[keyFn(a)] - indices[keyFn(b)]); }); return array; }; // permutes an array using a Knuth shuffle u.permute = function(a) { var m = a.length, swap, i; while (m) { i = Math.floor(Math.random() * m--); swap = a[m]; a[m] = a[i]; a[i] = swap; } }; // string functions u.pad = function(s, length, pos, padchar) { padchar = padchar || " "; var d = length - s.length; if (d <= 0) return s; switch (pos) { case 'left': return strrep(d, padchar) + s; case 'middle': case 'center': return strrep(Math.floor(d/2), padchar) + s + strrep(Math.ceil(d/2), padchar); default: return s + strrep(d, padchar); } }; function strrep(n, str) { var s = "", i; for (i=0; i<n; ++i) s += str; return s; } u.truncate = function(s, length, pos, word, ellipsis) { var len = s.length; if (len <= length) return s; ellipsis = ellipsis !== undefined ? String(ellipsis) : '\u2026'; var l = Math.max(0, length - ellipsis.length); switch (pos) { case 'left': return ellipsis + (word ? truncateOnWord(s,l,1) : s.slice(len-l)); case 'middle': case 'center': var l1 = Math.ceil(l/2), l2 = Math.floor(l/2); return (word ? truncateOnWord(s,l1) : s.slice(0,l1)) + ellipsis + (word ? truncateOnWord(s,l2,1) : s.slice(len-l2)); default: return (word ? truncateOnWord(s,l) : s.slice(0,l)) + ellipsis; } }; function truncateOnWord(s, len, rev) { var cnt = 0, tok = s.split(truncate_word_re); if (rev) { s = (tok = tok.reverse()) .filter(function(w) { cnt += w.length; return cnt <= len; }) .reverse(); } else { s = tok.filter(function(w) { cnt += w.length; return cnt <= len; }); } return s.length ? s.join('').trim() : tok[0].slice(0, len); } var truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/;