function LList(data) {
this.head = null;
this.tail = null;
if (data)
(Array.isArray(data) ? data : [].slice.apply(arguments))
.reverse().forEach(this.unshift, this);
}
LList.makeQuery = function(obj){
if (typeof obj === "undefined")
return function(el){
return typeof el === "undefined";
};
if (obj === null)
return function(el){
return el === null;
};
if (obj.constructor === Object) {
var fns = {};
Object.keys(obj).forEach(function(k){fns[k] = LList.makeQuery(obj[k]);});
return function(el){
return Object.keys(obj).every(function(k){return fns[k].call(el[k], el[k]);});
};
}
if (obj.constructor === Function)
return obj;
if (obj.constructor === RegExp)
return function(el){
return obj.test(el);
};
if (obj.constructor === String && /^[<>=!]/.test(obj))
obj = "return this " + obj;
if (obj.constructor === String && /^return/.test(obj))
return new Function(obj);
return function(el){
return el === obj;
};
};
LList.FIND_NODE = 1;
LList.FIND_HEAD = 2;
LList.FIND_PREV = 4;
LList.prototype.find = function(query, flags){
if (!flags) flags = LList.FIND_HEAD;
query = LList.makeQuery(query);
var x = this, y = null;
while (y = x, (x = x.tail) !== null) {
if (query.call(x.head, x.head)){
if (flags & LList.FIND_PREV) x = y;
if (flags & LList.FIND_HEAD) x = x.head;
break;
}
}
return x;
};
LList.prototype.filter = function(query){
query = LList.makeQuery(query);
var x = this, a = new LList();
while ((x = x.tail) !== null){
if (query.call(x.head, x.head))
a.unshift(x.head);
}
return a;
};
LList.prototype.map = function(fn){
fn = LList.makeQuery(fn);
return this.toArray().map(function(x){return fn.call(x, x);});
};
LList.prototype.unshift = function(x){
this.tail = {head: x, tail: this.tail};
return this;
};
LList.prototype.insert = function(a){
return {
after: function(b){
b = this.find(b, LList.FIND_NODE);
b.tail = {head: a, tail: b.tail};
return this;
}.bind(this),
before: function(b){
b = this.find(b, LList.FIND_NODE | LList.FIND_PREV);
b.tail = {head: a, tail: b.tail};
return this;
}.bind(this)
};
};
LList.prototype.remove = function(a){
a = this.find(a, LList.FIND_NODE | LList.FIND_PREV);
a.tail = a.tail.tail;
return this;
};
LList.prototype.toArray = function(){
var a = [], x = this;
while ((x = x.tail) !== null){
a.push(x.head);
}
return a;
};
LList.prototype.forEach = function(fn, scope){
var x = this;
while ((x = x.tail) !== null)
fn.call(scope || x.head, x.head);
return this;
};
LList.prototype.some = function(fn, scope){
var x = this;
while ((x = x.tail) !== null)
if (fn.call(scope || x.head, x.head)) return true;
return false;
};
LList.prototype.contains = function(a){
return this.some(function(b){return a === b;});
};
LList.intersect = function(a, b){
var c = new LList();
a.forEach(function(x){if (b.contains(x)) c.unshift(x);});
return c;
};
LList.union = function(a, b){
var c = new LList();
a.forEach(function(x){c.unshift(x);});
b.forEach(function(x){if (!b.contains(x)) c.unshift(x);});
return c;
};
LList.prototype.toString = function(){
return this.toArray().join(", ");
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script id="jsbin-javascript">
function LList(data) {
this.head = null;
this.tail = null;
if (data)
(Array.isArray(data) ? data : [].slice.apply(arguments))
.reverse().forEach(this.unshift, this);
}
LList.makeQuery = function(obj){
if (typeof obj === "undefined")
return function(el){
return typeof el === "undefined";
};
if (obj === null)
return function(el){
return el === null;
};
if (obj.constructor === Object) {
var fns = {};
Object.keys(obj).forEach(function(k){fns[k] = LList.makeQuery(obj[k]);});
return function(el){
return Object.keys(obj).every(function(k){return fns[k].call(el[k], el[k]);});
};
}
if (obj.constructor === Function)
return obj;
if (obj.constructor === RegExp)
return function(el){
return obj.test(el);
};
if (obj.constructor === String && /^[<>=!]/.test(obj))
obj = "return this " + obj;
if (obj.constructor === String && /^return/.test(obj))
return new Function(obj);
return function(el){
return el === obj;
};
};
LList.FIND_NODE = 1;
LList.FIND_HEAD = 2;
LList.FIND_PREV = 4;
LList.prototype.find = function(query, flags){
if (!flags) flags = LList.FIND_HEAD;
query = LList.makeQuery(query);
var x = this, y = null;
while (y = x, (x = x.tail) !== null) {
if (query.call(x.head, x.head)){
if (flags & LList.FIND_PREV) x = y;
if (flags & LList.FIND_HEAD) x = x.head;
break;
}
}
return x;
};
LList.prototype.filter = function(query){
query = LList.makeQuery(query);
var x = this, a = new LList();
while ((x = x.tail) !== null){
if (query.call(x.head, x.head))
a.unshift(x.head);
}
return a;
};
LList.prototype.map = function(fn){
fn = LList.makeQuery(fn);
return this.toArray().map(function(x){return fn.call(x, x);});
};
LList.prototype.unshift = function(x){
this.tail = {head: x, tail: this.tail};
return this;
};
LList.prototype.insert = function(a){
return {
after: function(b){
b = this.find(b, LList.FIND_NODE);
b.tail = {head: a, tail: b.tail};
return this;
}.bind(this),
before: function(b){
b = this.find(b, LList.FIND_NODE | LList.FIND_PREV);
b.tail = {head: a, tail: b.tail};
return this;
}.bind(this)
};
};
LList.prototype.remove = function(a){
a = this.find(a, LList.FIND_NODE | LList.FIND_PREV);
a.tail = a.tail.tail;
return this;
};
LList.prototype.toArray = function(){
var a = [], x = this;
while ((x = x.tail) !== null){
a.push(x.head);
}
return a;
};
LList.prototype.forEach = function(fn, scope){
var x = this;
while ((x = x.tail) !== null)
fn.call(scope || x.head, x.head);
return this;
};
LList.prototype.some = function(fn, scope){
var x = this;
while ((x = x.tail) !== null)
if (fn.call(scope || x.head, x.head)) return true;
return false;
};
LList.prototype.contains = function(a){
return this.some(function(b){return a === b;});
};
LList.intersect = function(a, b){
var c = new LList();
a.forEach(function(x){if (b.contains(x)) c.unshift(x);});
return c;
};
LList.union = function(a, b){
var c = new LList();
a.forEach(function(x){c.unshift(x);});
b.forEach(function(x){if (!b.contains(x)) c.unshift(x);});
return c;
};
LList.prototype.toString = function(){
return this.toArray().join(", ");
};
</script>
</body>
</html>