lists
Version:
A library of higher-order functions modeled after Haskell's Data.List module
593 lines (591 loc) • 17.8 kB
JavaScript
(function() {
var _version = '1.0.0', _style='Naive Recursion',
root = this, lists = {}, l = lists, _ = l._=undefined,
// Private helpers
b = bounce = function(f) {
while (f && typeof f === "function") {
f = f.apply(f.context, f.args);
}
return f;
},
clone = function(xs) {
var arr = typeof xs==="string" ? '' : []
l.each(xs, function(x){ arr.push(x); }); return arr;
},
notIt = function(xs, n) {
var arr = [];
l.each(xs, function(x,i) { if (i!=n) arr.push(xs[i])})
return arr;
},
excludeChar = function(cha,str) {
var res = [], t = '';
l.each(str, function(ch, i) {
if (ch == cha) { res.push(t); t='';
} else { t+=ch }
}); res.push(t);
return res
},
includeChar = function(cha, xs) {
var t = '';
l.each(xs, function(x, i) {
t+=x.concat(cha);
});
return t
},
elemBy = function(xs, eq, y) { // needs optimization
return l.nil(xs) ? false
: eq(y,l.head(xs)) || elemBy(l.tail(xs),eq,y)
};
// Basic Functions
l.append = function(xs, ys) {
return xs.concat(ys)
}
l.head = function(xs) {
return xs[0];
}
l.last = function(xs) {
return xs[xs.length-1];
}
l.init = function(xs) {
return notIt(xs, xs.length-1);
}
l.tail = function(xs) {
return notIt(xs, 0);
}
l.nil = function(xs) {
return typeof xs === 'undefined' || xs === null || xs.length === 0
? true : false
}
// List transformations
l.map = function(xs, f) {
function _map(xs, f, acc) {
return l.nil(xs) ? acc
: _map.bind(null, l.tail(xs), f, acc.concat([f(l.head(xs))]));
}
return b(_map.bind(null, xs, f, []));
}
l.rev = function(xs) {
function _rev(xs, acc) {
return l.nil(xs) ? acc
: _rev.bind(null, l.tail(xs), [l.head(xs)].concat(acc));
}
return b(_rev.bind(null, xs, []));
}
l.intersperse = function(sep, xs) {
function _intersperse(sep, xs, acc) {
return l.nil(xs)
? Array.isArray(acc) ? l.init(acc) : acc.substr(0, acc.length-1)
: Array.isArray(xs)
? _intersperse.bind(null, sep, l.tail(xs), acc.concat([l.head(xs)]).concat([sep]))
: _intersperse.bind(null, sep, xs.substring(1), acc.concat(l.head(xs)).concat(sep));
}
return b(_intersperse.bind(null, sep, xs, Array.isArray(xs)?[]:""));
}
l.intercalate = function(xs, xss) {
return l.flatten(l.intersperse(xs, xss))
}
l.transpose = function(xs) {
function _transpose(xs, acc) {
return l.nil(l.head(xs)) ? acc
: _transpose.bind(null, l.map(xs,l.tail), acc.concat([l.map(xs,l.head)]));
}
return b(_transpose.bind(null, xs, []));
}
l.subsequences = function(xs) {
return [[]].concat(nonEmptySubsequences(xs))
function nonEmptySubsequences(xs) {
return l.nil(xs) ? []
: [[l.head(xs)]].concat(l.foldr([],nonEmptySubsequences(l.tail(xs)),function(ys,r) {
return [ys].concat([[l.head(xs)].concat(ys)]).concat(r)
}))
}
}
l.permutations = function(xs) {
var perms=[], strFlag=false;
if (typeof xs==='string') {xs = xs.split(''); strFlag=true;}
function p(inp, data) {
var digit, cache = data || [];
l.each(inp, function(elem,i){
digit = inp.splice(i, 1)[0];
if (inp.length === 0) perms.push(cache.concat([digit]));
p(inp.slice(), cache.concat([digit]));
inp.splice(i, 0, digit);
});
return foldChs(perms);
}
function foldChs(perms){
return !strFlag ? perms
: lists.foldl([], perms, function(x,y){ return x.concat(y.join('')) })
}
return p(xs);
}
// Data.list Reducing l(folds/catamorphism)
l.foldl = l.reduce = function(acc, xs, f) {
function _foldl(acc, xs, f) {
return l.nil(xs) ? acc
: _foldl.bind(null, f(acc, l.head(xs)), l.tail(xs), f);
}
return b(_foldl.bind(null, acc, xs, f))
}
l.foldl1 = function(xs, f) {
return l.nil(xs) ? "lists.foldl1: empty list"
: l.foldl(l.head(xs),l.tail(xs),f)
}
l.foldr = l.reduceRight = function(acc, xs, f) {
return l.nil(xs) ? acc
: f(l.head(xs),(l.foldr(acc, l.tail(xs), f)))
}
l.foldr1 = function(xs, f) {
return l.nil(xs) ? "lists.foldr1: empty list"
: xs.length===1 ? l.head(xs)
: f(l.head(xs),l.foldr1(l.tail(xs),f))
}
// anamorphism
//pointwise function chop8(xs){ return l.unfold(l.nil,l.part(l.take,8,_),l.part(l.drop,8,_),xs) }
//function unfoldMap(xs,f) { return lists.unfold(lists.nil, lists.part(lists.pipe(f,lists.head),_), lists.part(lists.tail,_), xs)}
l.unfold = function(p,h,t,xs) {
return p(xs) ? []
: [h(xs)].concat(l.unfold(p,h,t,(t(xs))))
}
// Data.List Special folds
l.flatten = l.concat = function(xs) {
return l.foldr([], xs, l.append);
}
l.concatMap = function(xs, f) {
return l.flatten(l.map(xs,f))
}
l.and = function(xs) {
return l.nil(xs) ? true
: l.head(xs) && l.and(l.tail(xs))
}
l.or = function(xs) {
return l.nil(xs) ? false
: l.head(xs) || l.or(l.tail(xs))
}
l.all = function(xs, p) {
return l.nil(xs) ? true
: p(l.head(xs)) && l.all(l.tail(xs), p)
}
l.any = function(xs, p) {
return l.nil(xs) ? false
: p(l.head(xs)) || l.any(l.tail(xs), p)
}
l.sum = function(xs) {
return l.foldl(0, xs, function(x,y) { return x+y; });
}
l.product = function(xs) {
return l.foldl(1, xs, function(x,y) { return x*y; })
}
l.maximum = function(xs) {
return l.nil(xs) ? "Empty List"
: xs.length === 1 ? l.head(xs)
: l.head(xs) > l.maximum(l.tail(xs)) ? l.head(xs)
: l.maximum(l.tail(xs))
}
l.maxList = function(xss) {
return l.foldl([],xss,function(x,y){
var g = (x.length > y.length) ? g = x : g = y; return g
})
}
l.minimum = function(xs) {
return l.nil(xs) ? "Empty List"
: xs.length === 1 ? l.head(xs)
: l.head(xs) < l.minimum(l.tail(xs)) ? l.head(xs)
: l.minimum(l.tail(xs));
}
l.minList = function(xss) {
return l.foldl1(xss,function(x,y){
var g = (x.length < y.length) ? g = x : g = y; return g
})
}
// Data.List building lists
// list comprehension
l.scanl = function(q, xs, f) {
return [q].concat(function(q,xs,f) {
return l.nil(xs) ? xs
: l.scanl(f(q,l.head(xs)), l.tail(xs), f)
}(q,xs,f))
}
l.scanr = function(q, xs, f) {
return l.nil(xs) ? [q]
: [(f(l.head(xs), l.head(prev(q,xs,f))))].concat(prev(q,xs,f))
function prev(q, xs, f) {
return l.scanr(q, l.tail(xs), f)
}
}
l.mapAccumL = function(acc, xs, f) {
return l.nil(xs) ? [acc, []]
: [l.mapAccumL(f(acc,l.head(xs))[0],l.tail(xs),f)[0], [f(acc,l.head(xs))[1]].concat(l.mapAccumL(f(acc,l.head(xs))[0],l.tail(xs),f)[1])]
}
l.mapAccumR = function(acc, xs, f) {
return l.nil(xs) ? [acc, []]
: [f(l.mapAccumR(acc,l.tail(xs),f)[0],l.head(xs))[0],[f(l.mapAccumR(acc,l.tail(xs),f)[0],l.head(xs))[1]].concat(l.mapAccumR(acc,l.tail(xs),f)[1])]
}
l.iterate = function(x, stop, f) {
return stop === 0 ? []
: [x].concat(l.iterate(f(x),stop-1,f))
}
l.replicate = function(x, stop) {
return stop === 0 ? []
: [x].concat(l.replicate(x, stop-1))
}
l.cycle = function(xs, stop) {
return l.flatten(l.replicate(xs, stop))
}
// Data.List Sublists
l.dropWhile = function(xs, p) {
return l.nil(xs) ? []
: p(l.head(xs)) ? l.dropWhile(l.tail(xs), p)
: xs
}
l.takeWhile = function(xs, p) {
return l.nil(xs) ? []
: p(l.head(xs)) ? [l.head(xs)].concat(l.takeWhile(l.tail(xs), p))
: []
}
l.take = function(n, xs) {
return l.nil(xs) || n <= 0 ? []
: [l.head(xs)].concat(l.take(n-1, l.tail(xs)))
}
l.drop = function(n, xs) {
return l.nil(xs) || n <= 0 ? xs
: l.drop(n-1, l.tail(xs))
}
l.splitAt = function(n, xs) {
return [l.take(n,xs),l.drop(n,xs)]
}
l.span = function(xs, p) {
return [l.takeWhile(xs,p),l.dropWhile(xs,p)]
}
l.break = function(xs, p) {
return [l.takeWhile(xs,not), l.dropWhile(xs,not)]
function not(x) {
return !(p(x))
}
}
l.stripPrefix = function(xs, ys) {
return l.nil(xs) ? ys
: !l.nil(ys) ?
l.head(xs)==(l.head(ys)) ? l.stripPrefix(l.tail(xs),l.tail(ys))
: null
: null
}
l.group = function(xs) {
return l.groupBy(xs, function(x, y) {
return x === y;
})
}
l.inits = function(xs) {
return l.map(l.scanl([],xs,function(x,y) {
return [y].concat(x);
}), function(x) {
return l.rev(x)
});
}
l.tails = function(xs) {
return l.rev(l.inits(xs))
}
//Data.List Predecates
l.isPrefixOf = function(xs,ys) {
return l.nil(xs) ? true
: l.nil(ys) ? false
: l.head(xs)===l.head(ys) && l.isPrefixOf(l.tail(xs),l.tail(ys))
}
l.isSuffixOf = function(xs, ys) {
return l.isPrefixOf(l.rev(xs),l.rev(ys))
}
l.isInfixOf = function(xs, ys) { // :(
return l.nil(ys) ? false
: l.isPrefixOf(xs,ys) ? true
: l.isInfixOf(xs,l.tail(ys))
}
l.isSubsequenceOf = function(xs, ys) {
return l.nil(xs) ? true
: l.nil(ys) ? false
: l.head(xs).toString() == l.head(ys).toString()
? l.isSubsequenceOf(l.tail(xs), l.tail(ys))
: l.isSubsequenceOf(xs, l.tail(ys))
}
//Data.List searching by equality
l.elem = function(x, xs) {
return l.nil(xs) ? false
: x===(l.head(xs)) || l.elem(x,l.tail(xs))
}
l.notElem = function(x,xs) {
return !l.elem(x,xs)
}
l.lookup = function(key,xs) { // for associative arrays
return l.nil(key) || l.nil(xs) ? 'Nothing'
: key===l.head(xs)[0] ? l.head(xs)[1]
: l.lookup(key, l.tail(xs))
}
//Data.List Searching with a predicate
l.find = function(xs, p) {
return l.nil(xs) ? 'Nothing'
: p(l.head(xs)) ? l.head(xs)
: l.find(l.tail(xs), p)
}
l.filter = function(xs, p) {
return l.nil(xs) ? xs
: p(l.head(xs)) ? [l.head(xs)].concat(l.filter(l.tail(xs), p))
: l.filter(l.tail(xs), p)
}
l.partition = function(xs, p) {
return [(l.filter(xs, p)), (l.filter(xs,not))]
function not(x) {
return !(p(x))
}
}
// Data.List Indexing Lists
l.bang = function(i, xs) { // tail recursive with trampoline
return i < 0 ? -1
: l.nil(xs) ? -1
: i === 0 ? l.head(xs)
: b(l.bang.bind(null, i-1, l.tail(xs)));
}
l.elemIndex = l.indexOf = function(x, xs) { // tail recursive with trampoline
function _elemIndex(x,xs,acc) {
return l.nil(xs) ? -1
: x===l.head(xs) ? acc
: _elemIndex.bind(null,x,l.tail(xs),acc+1)
}
return b(_elemIndex.bind(null,x,xs,0))
}
l.elemIndices = function(x, xs) { // tail recursive with trampoline
function _elemIndices(x,xs,acc,arr) {
return l.nil(xs) ? arr
: x==l.head(xs) ? _elemIndices.bind(null,x,l.tail(xs),acc+1,arr.concat(acc))
: _elemIndices.bind(null,x,l.tail(xs),acc+1,arr)
}
return b(_elemIndices.bind(null,x,xs,0,[]))
}
l.findIndex = function(xs,p) { // tail recursive with trampoline
function _findIndex(xs,p,acc) {
return l.nil(xs) ? -1
: p(l.head(xs)) ? acc
: _findIndex.bind(null,l.tail(xs),p,acc+1)
}
return b(_findIndex.bind(null,xs,p,0))
}
l.findIndices = function(xs,p) { // tail recursive with trampoline
function _findIndices(xs,p,acc,arr) {
return l.nil(xs) ? arr
: p(l.head(xs)) ? _findIndices.bind(null,l.tail(xs), p, acc+1, arr.concat(acc))
: _findIndices.bind(null,l.tail(xs),p,acc+1, arr)
}
return b(_findIndices.bind(null,xs,p,0,[]))
}
l.zip = l.unzip = function(xss) {
var arr = new Array(l.maxList(xss).length);
for (var i = 0, len = arr.length; i < len; i++) {
arr[i] = l.map(xss,function(x){
return x[i]
})
}
return arr;
}
l.zipWith = function(xs, f) {
return l.map(l.zip(xs), f)
}
// Data.List - Special Lists - Functions on strings
l.lines = function(str) {
return l.genericExcludeChar('\u000A',str)
}
l.words = function(str) {
return l.genericExcludeChar('\u0020',str)
}
l.unlines = function(str) {
return includeChar('\u000A', str)
}
l.unwords = function(str) {
return includeChar('\u0020', str)
}
// Data.List Set Operations
l.nub = l.uniq = l.unique = function(xs) {
return l.nubBy(xs, function(x,y){ return x==y })
}
l.delete = function(x,xs) {
return l.deleteBy(x, xs, function(z,y) { return z.toString()==y.toString() })
}
l.diff = l.difference = function(xs,ys) {
return l.nil(ys) ? xs
: l.nil(xs) ? xs
: l.filter(xs, function(x) {
return l.notElem(x,ys)
});
}
l.union = function(xs,ys) {
return l.unionBy(xs,ys,function(x,y) { return x==y; });
}
l.intersect = function(xs,ys) {
return l.intersectBy(xs,ys,function(x,y) { return x==y; });
}
// Ordered lists
l.sort = function(xs) {
return l.sortBy(xs,l.compare);
}
l.insert = function(e, ls) {
return l.insertBy(e,ls,l.compare);
}
// Data.List Generalized Functions
l.unionBy = function(xs, ys, eq) {
return xs.concat(l.foldl(l.nubBy(ys,eq),xs,l.flip(l.part(l.deleteBy,_,_,eq))));
}
l.intersectBy = function(xs,ys,eq) {
return l.nil(xs) ? []
: l.nil(ys) ? []
: l.any(ys, l.part(eq,l.head(xs),_)) ? [l.head(xs)].concat(l.intersectBy(l.tail(xs),ys,eq))
: l.intersectBy(l.tail(xs),ys,eq)
}
l.groupBy = function(xs, p) {
return l.foldl([], xs, step)
function step(acc, ys) {
return l.nil(acc) ? [[ys]]
: p(l.head(l.last(acc)),ys) ? l.init(acc).concat([l.last(acc).concat([ys])])
: acc.concat([[ys]])
}
}
l.deleteFirstsBy = function(xs,ys,eq) {
return l.foldl(xs,ys,l.flip(l.part(l.deleteBy,_,_,eq)))
}
l.nubBy = function(ls, eq) {
return nubBye(ls,[])
function nubBye(ys,xs) {
return l.nil(ys) ? []
: elemBy(xs,eq,l.head(ys)) ? nubBye(l.tail(ys),xs)
: [l.head(ys)].concat(nubBye(l.tail(ys),[l.head(ys)].concat(xs)))
}
}
l.deleteBy = function(x, xs, eq) {
return l.nil(xs) ? []
: eq(x,l.head(xs)) ? l.tail(xs)
: [l.head(xs)].concat(l.deleteBy(x, l.tail(xs), eq))
}
// Data.List User-supplied comparison
l.sortBy = function(xs, cmp) {
return l.foldr([],xs,l.part(l.insertBy,_,_,cmp))
}
l.insertBy = function(x, xs, cmp) {
return l.nil(xs) ? [x]
: l.GT(cmp(x,l.head(xs))) ? [l.head(xs)].concat(l.insertBy(x,l.tail(xs),cmp))
: [x].concat(xs)
}
l.maximumBy = function(xs, cmp) {
return l.nil(xs) ? "lists.maximumBy: empty list"
: l.foldl1(xs,function(x,y){
return l.GT(cmp(x,y)) ? x : y
})
}
l.minimumBy = function(xs, cmp) {
return l.nil(xs) ? "lists.minimumBy: empty list"
: l.foldl1(xs,function(x,y){
return l.GT(cmp(x,y)) ? y : x
})
}
// functional utilities
l.genericExcludeChar = function(ch,str) {
return l.filter(excludeChar(ch,str),function(x) { return x != ''; })
}
l.each = l.forEach = function(xs, fn, caller) {
if (l.nil(xs)) return xs
var t = Array.isArray(t) || typeof t === "string" ? true : false;
if (t) {
for (var index = 0, len = xs.length; index < len; index++) {
fn.call(caller, xs[index], index, xs)
}
} else { // keys from obj
var keys = l.keys(xs), len = keys.length;
for (var index = 0; index < len; index++) {
fn.call(caller, xs[keys[index]], index, xs)
}
}
return xs;
}
l.keys = function(obj) {
var res = []
for (prop in obj) { if (obj.hasOwnProperty(prop)) res.push(prop) }
return res
}
l.enum = l.enumeration = function(x,y) {
var ls = [], i;
if (typeof y === 'undefined') { i = 0; y = x } else { i = x };
for (; i <= y; i++) { ls.push(i) }
return ls
}
l.composeL = l.pipe = function() {
var fns = arguments;
return function(result) {
for (var i = fns.length-1; i >= 0; i--) {
result = fns[i].call(this, result);
}
return result;
}
}
l.composeR = l.sequence = function() {
var fns = arguments;
return function(result) {
for (var i = 0; i < fns.length; i++) {
result = fns[i].call(this, result);
}
return result;
}
return result;
}
l.part = l.partial = function(fn) {
var slice = Array.prototype.slice, params = slice.call(arguments, 1)
return function() {
var partial = slice.call(arguments, 0), args = [], self = this;
for (var i = 0, len = params.length; i < len; i++) {
args[i] = params[i] === _ ? partial.shift() : params[i];
}
return fn.apply(self, args.concat(partial));
}
}
l.flip = function(fn) { // flip a function's arguments
var self = this;
return function() {
var args = Array.prototype.slice.call(arguments);
return fn.apply(self, args.reverse())
}
}
l.bubbleSort = function(xs) {
return bubSort(xs,0)
function bubSort(ys, i) {
return i == ys.length ? ys
: bubSort(bubSortI(ys),i+1)
}
function bubSortI(zs) {
return zs.length===1 ? [zs[0]]
: zs[0] > zs[1] ? [zs[1]].concat(bubSortI([zs[0]].concat(t(zs))))
: [zs[0]].concat(bubSortI([zs[1]].concat(t(zs))))
}
function t(xs) {
var arr = [];
l.each(xs,function(x,i){if (i!=0&&i!=1) arr.push(x)});
return arr;
}
}
l.mergeSort = function(xs) {
var split = mergeSplit(xs);
return xs.length>1 ? merge(l.mergeSort(split[0]),l.mergeSort(split[1])) : xs
function merge(xs,ys){
return l.nil(xs) ? ys : l.nil(ys) ? xs
: l.head(xs) < l.head(ys)
? [l.head(xs)].concat(merge(l.tail(xs),[l.head(ys)].concat(l.tail(ys))))
: [l.head(ys)].concat(merge([l.head(xs)].concat(l.tail(xs)),l.tail(ys)))}
function mergeSplit(xs){return [l.take(xs.length/2,xs),l.drop(xs.length/2,xs)]}
}
// Ordering
l.compare = function(a,b) { return a == b ? 'EQ' : a <= b ? 'LT' : 'GT'}
l.GT = function(val) { return val == 'GT'}
l.LT = function(val) { return val == 'LT'}
l.EQ = function(val) { return val == 'EQ'}
// platform boilerplate
typeof module !== 'undefined' && module.exports
? module.exports = lists : root.lists = lists;
if (typeof define === 'function' && define.amd) {
define('lists', [], function() {
return lists;
});
}
}.call(this));