Created
June 10, 2015 13:16
-
-
Save rushfrisby/6ee0e4ce3268bf87270c to your computer and use it in GitHub Desktop.
linq.js with the addition of CRUD methods
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
/*-------------------------------------------------------------------------- | |
* linq.js - LINQ for JavaScript | |
* ver 2.2.0.2 (Jan. 21th, 2011) | |
* | |
* created and maintained by neuecc <[email protected]> | |
* licensed under Microsoft Public License(Ms-PL) | |
* http://neue.cc/ | |
* http://linqjs.codeplex.com/ | |
*--------------------------------------------------------------------------*/ | |
Enumerable = (function () { | |
var Enumerable = function (getEnumerator) { | |
this.GetEnumerator = getEnumerator; | |
} | |
// Generator | |
Enumerable.Choice = function () // variable argument | |
{ | |
var args = (arguments[0] instanceof Array) ? arguments[0] : arguments; | |
return new Enumerable(function () { | |
return new IEnumerator( | |
Functions.Blank, | |
function () { | |
return this.Yield(args[Math.floor(Math.random() * args.length)]); | |
}, | |
Functions.Blank); | |
}); | |
} | |
Enumerable.Cycle = function () // variable argument | |
{ | |
var args = (arguments[0] instanceof Array) ? arguments[0] : arguments; | |
return new Enumerable(function () { | |
var index = 0; | |
return new IEnumerator( | |
Functions.Blank, | |
function () { | |
if (index >= args.length) index = 0; | |
return this.Yield(args[index++]); | |
}, | |
Functions.Blank); | |
}); | |
} | |
Enumerable.Empty = function () { | |
return new Enumerable(function () { | |
return new IEnumerator( | |
Functions.Blank, | |
function () { return false; }, | |
Functions.Blank); | |
}); | |
} | |
Enumerable.From = function (obj) { | |
if (obj == null) { | |
//return Enumerable.Empty(); | |
return new ArrayEnumerable([]); | |
} | |
if (obj instanceof Enumerable) { | |
return obj; | |
} | |
if (typeof obj == Types.Number || typeof obj == Types.Boolean) { | |
return ArrayEnumerable(Enumerable.Repeat(obj, 1).ToArray()); | |
} | |
if (typeof obj == Types.String) { | |
return new Enumerable(function () { | |
var index = 0; | |
return new IEnumerator( | |
Functions.Blank, | |
function () { | |
return (index < obj.length) ? this.Yield(obj.charAt(index++)) : false; | |
}, | |
Functions.Blank); | |
}); | |
} | |
if (typeof obj != Types.Function) { | |
// array or array like object | |
if (typeof obj.length == Types.Number) { | |
return new ArrayEnumerable(obj); | |
} | |
// JScript's IEnumerable | |
if (!(obj instanceof Object) && Utils.IsIEnumerable(obj)) { | |
return new Enumerable(function () { | |
var isFirst = true; | |
var enumerator; | |
return new IEnumerator( | |
function () { enumerator = new Enumerator(obj); }, | |
function () { | |
if (isFirst) isFirst = false; | |
else enumerator.moveNext(); | |
return (enumerator.atEnd()) ? false : this.Yield(enumerator.item()); | |
}, | |
Functions.Blank); | |
}); | |
} | |
} | |
// case function/object : Create KeyValuePair[] | |
return new Enumerable(function () { | |
var array = []; | |
var index = 0; | |
return new IEnumerator( | |
function () { | |
for (var key in obj) { | |
if (!(obj[key] instanceof Function)) { | |
array.push({ Key: key, Value: obj[key] }); | |
} | |
} | |
}, | |
function () { | |
return (index < array.length) | |
? this.Yield(array[index++]) | |
: false; | |
}, | |
Functions.Blank); | |
}); | |
}, | |
Enumerable.Return = function (element) { | |
return Enumerable.Repeat(element, 1); | |
} | |
// Overload:function(input, pattern) | |
// Overload:function(input, pattern, flags) | |
Enumerable.Matches = function (input, pattern, flags) { | |
if (flags == null) flags = ""; | |
if (pattern instanceof RegExp) { | |
flags += (pattern.ignoreCase) ? "i" : ""; | |
flags += (pattern.multiline) ? "m" : ""; | |
pattern = pattern.source; | |
} | |
if (flags.indexOf("g") === -1) flags += "g"; | |
return new Enumerable(function () { | |
var regex; | |
return new IEnumerator( | |
function () { regex = new RegExp(pattern, flags) }, | |
function () { | |
var match = regex.exec(input); | |
return (match) ? this.Yield(match) : false; | |
}, | |
Functions.Blank); | |
}); | |
} | |
// Overload:function(start, count) | |
// Overload:function(start, count, step) | |
Enumerable.Range = function (start, count, step) { | |
if (step == null) step = 1; | |
return Enumerable.ToInfinity(start, step).Take(count); | |
} | |
// Overload:function(start, count) | |
// Overload:function(start, count, step) | |
Enumerable.RangeDown = function (start, count, step) { | |
if (step == null) step = 1; | |
return Enumerable.ToNegativeInfinity(start, step).Take(count); | |
} | |
// Overload:function(start, to) | |
// Overload:function(start, to, step) | |
Enumerable.RangeTo = function (start, to, step) { | |
if (step == null) step = 1; | |
return (start < to) | |
? Enumerable.ToInfinity(start, step).TakeWhile(function (i) { return i <= to; }) | |
: Enumerable.ToNegativeInfinity(start, step).TakeWhile(function (i) { return i >= to; }) | |
} | |
// Overload:function(obj) | |
// Overload:function(obj, num) | |
Enumerable.Repeat = function (obj, num) { | |
if (num != null) return Enumerable.Repeat(obj).Take(num); | |
return new Enumerable(function () { | |
return new IEnumerator( | |
Functions.Blank, | |
function () { return this.Yield(obj); }, | |
Functions.Blank); | |
}); | |
} | |
Enumerable.RepeatWithFinalize = function (initializer, finalizer) { | |
initializer = Utils.CreateLambda(initializer); | |
finalizer = Utils.CreateLambda(finalizer); | |
return new Enumerable(function () { | |
var element; | |
return new IEnumerator( | |
function () { element = initializer(); }, | |
function () { return this.Yield(element); }, | |
function () { | |
if (element != null) { | |
finalizer(element); | |
element = null; | |
} | |
}); | |
}); | |
} | |
// Overload:function(func) | |
// Overload:function(func, count) | |
Enumerable.Generate = function (func, count) { | |
if (count != null) return Enumerable.Generate(func).Take(count); | |
func = Utils.CreateLambda(func); | |
return new Enumerable(function () { | |
return new IEnumerator( | |
Functions.Blank, | |
function () { return this.Yield(func()); }, | |
Functions.Blank); | |
}); | |
} | |
// Overload:function() | |
// Overload:function(start) | |
// Overload:function(start, step) | |
Enumerable.ToInfinity = function (start, step) { | |
if (start == null) start = 0; | |
if (step == null) step = 1; | |
return new Enumerable(function () { | |
var value; | |
return new IEnumerator( | |
function () { value = start - step }, | |
function () { return this.Yield(value += step); }, | |
Functions.Blank); | |
}); | |
} | |
// Overload:function() | |
// Overload:function(start) | |
// Overload:function(start, step) | |
Enumerable.ToNegativeInfinity = function (start, step) { | |
if (start == null) start = 0; | |
if (step == null) step = 1; | |
return new Enumerable(function () { | |
var value; | |
return new IEnumerator( | |
function () { value = start + step }, | |
function () { return this.Yield(value -= step); }, | |
Functions.Blank); | |
}); | |
} | |
Enumerable.Unfold = function (seed, func) { | |
func = Utils.CreateLambda(func); | |
return new Enumerable(function () { | |
var isFirst = true; | |
var value; | |
return new IEnumerator( | |
Functions.Blank, | |
function () { | |
if (isFirst) { | |
isFirst = false; | |
value = seed; | |
return this.Yield(value); | |
} | |
value = func(value); | |
return this.Yield(value); | |
}, | |
Functions.Blank); | |
}); | |
} | |
// Extension Methods | |
Enumerable.prototype = | |
{ | |
/* Projection and Filtering Methods */ | |
// Overload:function(func) | |
// Overload:function(func, resultSelector<element>) | |
// Overload:function(func, resultSelector<element, nestLevel>) | |
CascadeBreadthFirst: function (func, resultSelector) { | |
var source = this; | |
func = Utils.CreateLambda(func); | |
resultSelector = Utils.CreateLambda(resultSelector); | |
return new Enumerable(function () { | |
var enumerator; | |
var nestLevel = 0; | |
var buffer = []; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (true) { | |
if (enumerator.MoveNext()) { | |
buffer.push(enumerator.Current()); | |
return this.Yield(resultSelector(enumerator.Current(), nestLevel)); | |
} | |
var next = Enumerable.From(buffer).SelectMany(function (x) { return func(x); }); | |
if (!next.Any()) { | |
return false; | |
} | |
else { | |
nestLevel++; | |
buffer = []; | |
Utils.Dispose(enumerator); | |
enumerator = next.GetEnumerator(); | |
} | |
} | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
// Overload:function(func) | |
// Overload:function(func, resultSelector<element>) | |
// Overload:function(func, resultSelector<element, nestLevel>) | |
CascadeDepthFirst: function (func, resultSelector) { | |
var source = this; | |
func = Utils.CreateLambda(func); | |
resultSelector = Utils.CreateLambda(resultSelector); | |
return new Enumerable(function () { | |
var enumeratorStack = []; | |
var enumerator; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (true) { | |
if (enumerator.MoveNext()) { | |
var value = resultSelector(enumerator.Current(), enumeratorStack.length); | |
enumeratorStack.push(enumerator); | |
enumerator = Enumerable.From(func(enumerator.Current())).GetEnumerator(); | |
return this.Yield(value); | |
} | |
if (enumeratorStack.length <= 0) return false; | |
Utils.Dispose(enumerator); | |
enumerator = enumeratorStack.pop(); | |
} | |
}, | |
function () { | |
try { Utils.Dispose(enumerator); } | |
finally { Enumerable.From(enumeratorStack).ForEach(function (s) { s.Dispose(); }) } | |
}); | |
}); | |
}, | |
Flatten: function () { | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var middleEnumerator = null; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (true) { | |
if (middleEnumerator != null) { | |
if (middleEnumerator.MoveNext()) { | |
return this.Yield(middleEnumerator.Current()); | |
} | |
else { | |
middleEnumerator = null; | |
} | |
} | |
if (enumerator.MoveNext()) { | |
if (enumerator.Current() instanceof Array) { | |
Utils.Dispose(middleEnumerator); | |
middleEnumerator = Enumerable.From(enumerator.Current()) | |
.SelectMany(Functions.Identity) | |
.Flatten() | |
.GetEnumerator(); | |
continue; | |
} | |
else { | |
return this.Yield(enumerator.Current()); | |
} | |
} | |
return false; | |
} | |
}, | |
function () { | |
try { Utils.Dispose(enumerator); } | |
finally { Utils.Dispose(middleEnumerator); } | |
}); | |
}); | |
}, | |
Pairwise: function (selector) { | |
var source = this; | |
selector = Utils.CreateLambda(selector); | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
enumerator.MoveNext(); | |
}, | |
function () { | |
var prev = enumerator.Current(); | |
return (enumerator.MoveNext()) | |
? this.Yield(selector(prev, enumerator.Current())) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
// Overload:function(func) | |
// Overload:function(seed,func<value,element>) | |
// Overload:function(seed,func<value,element>,resultSelector) | |
Scan: function (seed, func, resultSelector) { | |
if (resultSelector != null) return this.Scan(seed, func).Select(resultSelector); | |
var isUseSeed; | |
if (func == null) { | |
func = Utils.CreateLambda(seed); // arguments[0] | |
isUseSeed = false; | |
} | |
else { | |
func = Utils.CreateLambda(func); | |
isUseSeed = true; | |
} | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var value; | |
var isFirst = true; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
if (isFirst) { | |
isFirst = false; | |
if (!isUseSeed) { | |
if (enumerator.MoveNext()) { | |
return this.Yield(value = enumerator.Current()); | |
} | |
} | |
else { | |
return this.Yield(value = seed); | |
} | |
} | |
return (enumerator.MoveNext()) | |
? this.Yield(value = func(value, enumerator.Current())) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
// Overload:function(selector<element>) | |
// Overload:function(selector<element,index>) | |
Select: function (selector) { | |
var source = this; | |
selector = Utils.CreateLambda(selector); | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
return (enumerator.MoveNext()) | |
? this.Yield(selector(enumerator.Current(), index++)) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function(collectionSelector<element>) | |
// Overload:function(collectionSelector<element,index>) | |
// Overload:function(collectionSelector<element>,resultSelector) | |
// Overload:function(collectionSelector<element,index>,resultSelector) | |
SelectMany: function (collectionSelector, resultSelector) { | |
var source = this; | |
collectionSelector = Utils.CreateLambda(collectionSelector); | |
if (resultSelector == null) resultSelector = function (a, b) { return b; } | |
resultSelector = Utils.CreateLambda(resultSelector); | |
return new Enumerable(function () { | |
var enumerator; | |
var middleEnumerator = undefined; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
if (middleEnumerator === undefined) { | |
if (!enumerator.MoveNext()) return false; | |
} | |
do { | |
if (middleEnumerator == null) { | |
var middleSeq = collectionSelector(enumerator.Current(), index++); | |
middleEnumerator = Enumerable.From(middleSeq).GetEnumerator(); | |
} | |
if (middleEnumerator.MoveNext()) { | |
return this.Yield(resultSelector(enumerator.Current(), middleEnumerator.Current())); | |
} | |
Utils.Dispose(middleEnumerator); | |
middleEnumerator = null; | |
} while (enumerator.MoveNext()) | |
return false; | |
}, | |
function () { | |
try { Utils.Dispose(enumerator); } | |
finally { Utils.Dispose(middleEnumerator); } | |
}) | |
}); | |
}, | |
// Overload:function(predicate<element>) | |
// Overload:function(predicate<element,index>) | |
Where: function (predicate) { | |
predicate = Utils.CreateLambda(predicate); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (enumerator.MoveNext()) { | |
if (predicate(enumerator.Current(), index++)) { | |
return this.Yield(enumerator.Current()); | |
} | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
OfType: function (type) { | |
var typeName; | |
switch (type) { | |
case Number: typeName = Types.Number; break; | |
case String: typeName = Types.String; break; | |
case Boolean: typeName = Types.Boolean; break; | |
case Function: typeName = Types.Function; break; | |
default: typeName = null; break; | |
} | |
return (typeName === null) | |
? this.Where(function (x) { return x instanceof type }) | |
: this.Where(function (x) { return typeof x === typeName }); | |
}, | |
// Overload:function(second,selector<outer,inner>) | |
// Overload:function(second,selector<outer,inner,index>) | |
Zip: function (second, selector) { | |
selector = Utils.CreateLambda(selector); | |
var source = this; | |
return new Enumerable(function () { | |
var firstEnumerator; | |
var secondEnumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { | |
firstEnumerator = source.GetEnumerator(); | |
secondEnumerator = Enumerable.From(second).GetEnumerator(); | |
}, | |
function () { | |
if (firstEnumerator.MoveNext() && secondEnumerator.MoveNext()) { | |
return this.Yield(selector(firstEnumerator.Current(), secondEnumerator.Current(), index++)); | |
} | |
return false; | |
}, | |
function () { | |
try { Utils.Dispose(firstEnumerator); } | |
finally { Utils.Dispose(secondEnumerator); } | |
}) | |
}); | |
}, | |
/* Join Methods */ | |
// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) | |
// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) | |
Join: function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { | |
outerKeySelector = Utils.CreateLambda(outerKeySelector); | |
innerKeySelector = Utils.CreateLambda(innerKeySelector); | |
resultSelector = Utils.CreateLambda(resultSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var source = this; | |
return new Enumerable(function () { | |
var outerEnumerator; | |
var lookup; | |
var innerElements = null; | |
var innerCount = 0; | |
return new IEnumerator( | |
function () { | |
outerEnumerator = source.GetEnumerator(); | |
lookup = Enumerable.From(inner).ToLookup(innerKeySelector, Functions.Identity, compareSelector); | |
}, | |
function () { | |
while (true) { | |
if (innerElements != null) { | |
var innerElement = innerElements[innerCount++]; | |
if (innerElement !== undefined) { | |
return this.Yield(resultSelector(outerEnumerator.Current(), innerElement)); | |
} | |
innerElement = null; | |
innerCount = 0; | |
} | |
if (outerEnumerator.MoveNext()) { | |
var key = outerKeySelector(outerEnumerator.Current()); | |
innerElements = lookup.Get(key).ToArray(); | |
} | |
else { | |
return false; | |
} | |
} | |
}, | |
function () { Utils.Dispose(outerEnumerator); }) | |
}); | |
}, | |
// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) | |
// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) | |
GroupJoin: function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { | |
outerKeySelector = Utils.CreateLambda(outerKeySelector); | |
innerKeySelector = Utils.CreateLambda(innerKeySelector); | |
resultSelector = Utils.CreateLambda(resultSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator = source.GetEnumerator(); | |
var lookup = null; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
lookup = Enumerable.From(inner).ToLookup(innerKeySelector, Functions.Identity, compareSelector); | |
}, | |
function () { | |
if (enumerator.MoveNext()) { | |
var innerElement = lookup.Get(outerKeySelector(enumerator.Current())); | |
return this.Yield(resultSelector(enumerator.Current(), innerElement)); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
/* Set Methods */ | |
All: function (predicate) { | |
predicate = Utils.CreateLambda(predicate); | |
var result = true; | |
this.ForEach(function (x) { | |
if (!predicate(x)) { | |
result = false; | |
return false; // break | |
} | |
}); | |
return result; | |
}, | |
// Overload:function() | |
// Overload:function(predicate) | |
Any: function (predicate) { | |
predicate = Utils.CreateLambda(predicate); | |
var enumerator = this.GetEnumerator(); | |
try { | |
if (arguments.length == 0) return enumerator.MoveNext(); // case:function() | |
while (enumerator.MoveNext()) // case:function(predicate) | |
{ | |
if (predicate(enumerator.Current())) return true; | |
} | |
return false; | |
} | |
finally { Utils.Dispose(enumerator); } | |
}, | |
Concat: function (second) { | |
var source = this; | |
return new Enumerable(function () { | |
var firstEnumerator; | |
var secondEnumerator; | |
return new IEnumerator( | |
function () { firstEnumerator = source.GetEnumerator(); }, | |
function () { | |
if (secondEnumerator == null) { | |
if (firstEnumerator.MoveNext()) return this.Yield(firstEnumerator.Current()); | |
secondEnumerator = Enumerable.From(second).GetEnumerator(); | |
} | |
if (secondEnumerator.MoveNext()) return this.Yield(secondEnumerator.Current()); | |
return false; | |
}, | |
function () { | |
try { Utils.Dispose(firstEnumerator); } | |
finally { Utils.Dispose(secondEnumerator); } | |
}) | |
}); | |
}, | |
Insert: function (index, second) { | |
var source = this; | |
return new Enumerable(function () { | |
var firstEnumerator; | |
var secondEnumerator; | |
var count = 0; | |
var isEnumerated = false; | |
return new IEnumerator( | |
function () { | |
firstEnumerator = source.GetEnumerator(); | |
secondEnumerator = Enumerable.From(second).GetEnumerator(); | |
}, | |
function () { | |
if (count == index && secondEnumerator.MoveNext()) { | |
isEnumerated = true; | |
return this.Yield(secondEnumerator.Current()); | |
} | |
if (firstEnumerator.MoveNext()) { | |
count++; | |
return this.Yield(firstEnumerator.Current()); | |
} | |
if (!isEnumerated && secondEnumerator.MoveNext()) { | |
return this.Yield(secondEnumerator.Current()); | |
} | |
return false; | |
}, | |
function () { | |
try { Utils.Dispose(firstEnumerator); } | |
finally { Utils.Dispose(secondEnumerator); } | |
}) | |
}); | |
}, | |
Alternate: function (value) { | |
value = Enumerable.Return(value); | |
return this.SelectMany(function (elem) { | |
return Enumerable.Return(elem).Concat(value); | |
}).TakeExceptLast(); | |
}, | |
// Overload:function(value) | |
// Overload:function(value, compareSelector) | |
Contains: function (value, compareSelector) { | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var enumerator = this.GetEnumerator(); | |
try { | |
while (enumerator.MoveNext()) { | |
if (compareSelector(enumerator.Current()) === value) return true; | |
} | |
return false; | |
} | |
finally { Utils.Dispose(enumerator) } | |
}, | |
DefaultIfEmpty: function (defaultValue) { | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var isFirst = true; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
if (enumerator.MoveNext()) { | |
isFirst = false; | |
return this.Yield(enumerator.Current()); | |
} | |
else if (isFirst) { | |
isFirst = false; | |
return this.Yield(defaultValue); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function() | |
// Overload:function(compareSelector) | |
Distinct: function (compareSelector) { | |
return this.Except(Enumerable.Empty(), compareSelector); | |
}, | |
// Overload:function(second) | |
// Overload:function(second, compareSelector) | |
Except: function (second, compareSelector) { | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var keys; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
keys = new Dictionary(compareSelector); | |
Enumerable.From(second).ForEach(function (key) { keys.Add(key); }); | |
}, | |
function () { | |
while (enumerator.MoveNext()) { | |
var current = enumerator.Current(); | |
if (!keys.Contains(current)) { | |
keys.Add(current); | |
return this.Yield(current); | |
} | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function(second) | |
// Overload:function(second, compareSelector) | |
Intersect: function (second, compareSelector) { | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var keys; | |
var outs; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
keys = new Dictionary(compareSelector); | |
Enumerable.From(second).ForEach(function (key) { keys.Add(key); }); | |
outs = new Dictionary(compareSelector); | |
}, | |
function () { | |
while (enumerator.MoveNext()) { | |
var current = enumerator.Current(); | |
if (!outs.Contains(current) && keys.Contains(current)) { | |
outs.Add(current); | |
return this.Yield(current); | |
} | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function(second) | |
// Overload:function(second, compareSelector) | |
SequenceEqual: function (second, compareSelector) { | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var firstEnumerator = this.GetEnumerator(); | |
try { | |
var secondEnumerator = Enumerable.From(second).GetEnumerator(); | |
try { | |
while (firstEnumerator.MoveNext()) { | |
if (!secondEnumerator.MoveNext() | |
|| compareSelector(firstEnumerator.Current()) !== compareSelector(secondEnumerator.Current())) { | |
return false; | |
} | |
} | |
if (secondEnumerator.MoveNext()) return false; | |
return true; | |
} | |
finally { Utils.Dispose(secondEnumerator); } | |
} | |
finally { Utils.Dispose(firstEnumerator); } | |
}, | |
Union: function (second, compareSelector) { | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var source = this; | |
return new Enumerable(function () { | |
var firstEnumerator; | |
var secondEnumerator; | |
var keys; | |
return new IEnumerator( | |
function () { | |
firstEnumerator = source.GetEnumerator(); | |
keys = new Dictionary(compareSelector); | |
}, | |
function () { | |
var current; | |
if (secondEnumerator === undefined) { | |
while (firstEnumerator.MoveNext()) { | |
current = firstEnumerator.Current(); | |
if (!keys.Contains(current)) { | |
keys.Add(current); | |
return this.Yield(current); | |
} | |
} | |
secondEnumerator = Enumerable.From(second).GetEnumerator(); | |
} | |
while (secondEnumerator.MoveNext()) { | |
current = secondEnumerator.Current(); | |
if (!keys.Contains(current)) { | |
keys.Add(current); | |
return this.Yield(current); | |
} | |
} | |
return false; | |
}, | |
function () { | |
try { Utils.Dispose(firstEnumerator); } | |
finally { Utils.Dispose(secondEnumerator); } | |
}) | |
}); | |
}, | |
/* Ordering Methods */ | |
OrderBy: function (keySelector) { | |
return new OrderedEnumerable(this, keySelector, false); | |
}, | |
OrderByDescending: function (keySelector) { | |
return new OrderedEnumerable(this, keySelector, true); | |
}, | |
Reverse: function () { | |
var source = this; | |
return new Enumerable(function () { | |
var buffer; | |
var index; | |
return new IEnumerator( | |
function () { | |
buffer = source.ToArray(); | |
index = buffer.length; | |
}, | |
function () { | |
return (index > 0) | |
? this.Yield(buffer[--index]) | |
: false; | |
}, | |
Functions.Blank) | |
}); | |
}, | |
Shuffle: function () { | |
var source = this; | |
return new Enumerable(function () { | |
var buffer; | |
return new IEnumerator( | |
function () { buffer = source.ToArray(); }, | |
function () { | |
if (buffer.length > 0) { | |
var i = Math.floor(Math.random() * buffer.length); | |
return this.Yield(buffer.splice(i, 1)[0]); | |
} | |
return false; | |
}, | |
Functions.Blank) | |
}); | |
}, | |
/* Grouping Methods */ | |
// Overload:function(keySelector) | |
// Overload:function(keySelector,elementSelector) | |
// Overload:function(keySelector,elementSelector,resultSelector) | |
// Overload:function(keySelector,elementSelector,resultSelector,compareSelector) | |
GroupBy: function (keySelector, elementSelector, resultSelector, compareSelector) { | |
var source = this; | |
keySelector = Utils.CreateLambda(keySelector); | |
elementSelector = Utils.CreateLambda(elementSelector); | |
if (resultSelector != null) resultSelector = Utils.CreateLambda(resultSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { | |
enumerator = source.ToLookup(keySelector, elementSelector, compareSelector) | |
.ToEnumerable() | |
.GetEnumerator(); | |
}, | |
function () { | |
while (enumerator.MoveNext()) { | |
return (resultSelector == null) | |
? this.Yield(enumerator.Current()) | |
: this.Yield(resultSelector(enumerator.Current().Key(), enumerator.Current())); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function(keySelector) | |
// Overload:function(keySelector,elementSelector) | |
// Overload:function(keySelector,elementSelector,resultSelector) | |
// Overload:function(keySelector,elementSelector,resultSelector,compareSelector) | |
PartitionBy: function (keySelector, elementSelector, resultSelector, compareSelector) { | |
var source = this; | |
keySelector = Utils.CreateLambda(keySelector); | |
elementSelector = Utils.CreateLambda(elementSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var hasResultSelector; | |
if (resultSelector == null) { | |
hasResultSelector = false; | |
resultSelector = function (key, group) { return new Grouping(key, group) } | |
} | |
else { | |
hasResultSelector = true; | |
resultSelector = Utils.CreateLambda(resultSelector); | |
} | |
return new Enumerable(function () { | |
var enumerator; | |
var key; | |
var compareKey; | |
var group = []; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
if (enumerator.MoveNext()) { | |
key = keySelector(enumerator.Current()); | |
compareKey = compareSelector(key); | |
group.push(elementSelector(enumerator.Current())); | |
} | |
}, | |
function () { | |
var hasNext; | |
while ((hasNext = enumerator.MoveNext()) == true) { | |
if (compareKey === compareSelector(keySelector(enumerator.Current()))) { | |
group.push(elementSelector(enumerator.Current())); | |
} | |
else break; | |
} | |
if (group.length > 0) { | |
var result = (hasResultSelector) | |
? resultSelector(key, Enumerable.From(group)) | |
: resultSelector(key, group); | |
if (hasNext) { | |
key = keySelector(enumerator.Current()); | |
compareKey = compareSelector(key); | |
group = [elementSelector(enumerator.Current())]; | |
} | |
else group = []; | |
return this.Yield(result); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
BufferWithCount: function (count) { | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
var array = []; | |
var index = 0; | |
while (enumerator.MoveNext()) { | |
array.push(enumerator.Current()); | |
if (++index >= count) return this.Yield(array); | |
} | |
if (array.length > 0) return this.Yield(array); | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
/* Aggregate Methods */ | |
// Overload:function(func) | |
// Overload:function(seed,func) | |
// Overload:function(seed,func,resultSelector) | |
Aggregate: function (seed, func, resultSelector) { | |
return this.Scan(seed, func, resultSelector).Last(); | |
}, | |
// Overload:function() | |
// Overload:function(selector) | |
Average: function (selector) { | |
selector = Utils.CreateLambda(selector); | |
var sum = 0; | |
var count = 0; | |
this.ForEach(function (x) { | |
sum += selector(x); | |
++count; | |
}); | |
return sum / count; | |
}, | |
// Overload:function() | |
// Overload:function(predicate) | |
Count: function (predicate) { | |
predicate = (predicate == null) ? Functions.True : Utils.CreateLambda(predicate); | |
var count = 0; | |
this.ForEach(function (x, i) { | |
if (predicate(x, i))++count; | |
}); | |
return count; | |
}, | |
// Overload:function() | |
// Overload:function(selector) | |
Max: function (selector) { | |
if (selector == null) selector = Functions.Identity; | |
return this.Select(selector).Aggregate(function (a, b) { return (a > b) ? a : b; }); | |
}, | |
// Overload:function() | |
// Overload:function(selector) | |
Min: function (selector) { | |
if (selector == null) selector = Functions.Identity; | |
return this.Select(selector).Aggregate(function (a, b) { return (a < b) ? a : b; }); | |
}, | |
MaxBy: function (keySelector) { | |
keySelector = Utils.CreateLambda(keySelector); | |
return this.Aggregate(function (a, b) { return (keySelector(a) > keySelector(b)) ? a : b }); | |
}, | |
MinBy: function (keySelector) { | |
keySelector = Utils.CreateLambda(keySelector); | |
return this.Aggregate(function (a, b) { return (keySelector(a) < keySelector(b)) ? a : b }); | |
}, | |
// Overload:function() | |
// Overload:function(selector) | |
Sum: function (selector) { | |
if (selector == null) selector = Functions.Identity; | |
return this.Select(selector).Aggregate(0, function (a, b) { return a + b; }); | |
}, | |
/* Paging Methods */ | |
ElementAt: function (index) { | |
var value; | |
var found = false; | |
this.ForEach(function (x, i) { | |
if (i == index) { | |
value = x; | |
found = true; | |
return false; | |
} | |
}); | |
if (!found) throw new Error("index is less than 0 or greater than or equal to the number of elements in source."); | |
return value; | |
}, | |
ElementAtOrDefault: function (index, defaultValue) { | |
var value; | |
var found = false; | |
this.ForEach(function (x, i) { | |
if (i == index) { | |
value = x; | |
found = true; | |
return false; | |
} | |
}); | |
return (!found) ? defaultValue : value; | |
}, | |
// Overload:function() | |
// Overload:function(predicate) | |
First: function (predicate) { | |
if (predicate != null) return this.Where(predicate).First(); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
value = x; | |
found = true; | |
return false; | |
}); | |
if (!found) throw new Error("First:No element satisfies the condition."); | |
return value; | |
}, | |
// Overload:function(defaultValue) | |
// Overload:function(defaultValue,predicate) | |
FirstOrDefault: function (defaultValue, predicate) { | |
if (predicate != null) return this.Where(predicate).FirstOrDefault(defaultValue); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
value = x; | |
found = true; | |
return false; | |
}); | |
return (!found) ? defaultValue : value; | |
}, | |
// Overload:function() | |
// Overload:function(predicate) | |
Last: function (predicate) { | |
if (predicate != null) return this.Where(predicate).Last(); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
found = true; | |
value = x; | |
}); | |
if (!found) throw new Error("Last:No element satisfies the condition."); | |
return value; | |
}, | |
// Overload:function(defaultValue) | |
// Overload:function(defaultValue,predicate) | |
LastOrDefault: function (defaultValue, predicate) { | |
if (predicate != null) return this.Where(predicate).LastOrDefault(defaultValue); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
found = true; | |
value = x; | |
}); | |
return (!found) ? defaultValue : value; | |
}, | |
// Overload:function() | |
// Overload:function(predicate) | |
Single: function (predicate) { | |
if (predicate != null) return this.Where(predicate).Single(); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
if (!found) { | |
found = true; | |
value = x; | |
} | |
else throw new Error("Single:sequence contains more than one element."); | |
}); | |
if (!found) throw new Error("Single:No element satisfies the condition."); | |
return value; | |
}, | |
// Overload:function(defaultValue) | |
// Overload:function(defaultValue,predicate) | |
SingleOrDefault: function (defaultValue, predicate) { | |
if (predicate != null) return this.Where(predicate).SingleOrDefault(defaultValue); | |
var value; | |
var found = false; | |
this.ForEach(function (x) { | |
if (!found) { | |
found = true; | |
value = x; | |
} | |
else throw new Error("Single:sequence contains more than one element."); | |
}); | |
return (!found) ? defaultValue : value; | |
}, | |
Skip: function (count) { | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { | |
enumerator = source.GetEnumerator(); | |
while (index++ < count && enumerator.MoveNext()) { }; | |
}, | |
function () { | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
// Overload:function(predicate<element>) | |
// Overload:function(predicate<element,index>) | |
SkipWhile: function (predicate) { | |
predicate = Utils.CreateLambda(predicate); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
var isSkipEnd = false; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (!isSkipEnd) { | |
if (enumerator.MoveNext()) { | |
if (!predicate(enumerator.Current(), index++)) { | |
isSkipEnd = true; | |
return this.Yield(enumerator.Current()); | |
} | |
continue; | |
} | |
else return false; | |
} | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
Take: function (count) { | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
return (index++ < count && enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); } | |
) | |
}); | |
}, | |
// Overload:function(predicate<element>) | |
// Overload:function(predicate<element,index>) | |
TakeWhile: function (predicate) { | |
predicate = Utils.CreateLambda(predicate); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
return (enumerator.MoveNext() && predicate(enumerator.Current(), index++)) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
// Overload:function() | |
// Overload:function(count) | |
TakeExceptLast: function (count) { | |
if (count == null) count = 1; | |
var source = this; | |
return new Enumerable(function () { | |
if (count <= 0) return source.GetEnumerator(); // do nothing | |
var enumerator; | |
var q = []; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
while (enumerator.MoveNext()) { | |
if (q.length == count) { | |
q.push(enumerator.Current()); | |
return this.Yield(q.shift()); | |
} | |
q.push(enumerator.Current()); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
TakeFromLast: function (count) { | |
if (count <= 0 || count == null) return Enumerable.Empty(); | |
var source = this; | |
return new Enumerable(function () { | |
var sourceEnumerator; | |
var enumerator; | |
var q = []; | |
return new IEnumerator( | |
function () { sourceEnumerator = source.GetEnumerator(); }, | |
function () { | |
while (sourceEnumerator.MoveNext()) { | |
if (q.length == count) q.shift() | |
q.push(sourceEnumerator.Current()); | |
} | |
if (enumerator == null) { | |
enumerator = Enumerable.From(q).GetEnumerator(); | |
} | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
IndexOf: function (item) { | |
var found = null; | |
this.ForEach(function (x, i) { | |
if (x === item) { | |
found = i; | |
return true; | |
} | |
}); | |
return (found !== null) ? found : -1; | |
}, | |
LastIndexOf: function (item) { | |
var result = -1; | |
this.ForEach(function (x, i) { | |
if (x === item) result = i; | |
}); | |
return result; | |
}, | |
/* Convert Methods */ | |
ToArray: function () { | |
var array = []; | |
this.ForEach(function (x) { array.push(x) }); | |
return array; | |
}, | |
// Overload:function(keySelector) | |
// Overload:function(keySelector, elementSelector) | |
// Overload:function(keySelector, elementSelector, compareSelector) | |
ToLookup: function (keySelector, elementSelector, compareSelector) { | |
keySelector = Utils.CreateLambda(keySelector); | |
elementSelector = Utils.CreateLambda(elementSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var dict = new Dictionary(compareSelector); | |
this.ForEach(function (x) { | |
var key = keySelector(x); | |
var element = elementSelector(x); | |
var array = dict.Get(key); | |
if (array !== undefined) array.push(element); | |
else dict.Add(key, [element]); | |
}); | |
return new Lookup(dict); | |
}, | |
ToObject: function (keySelector, elementSelector) { | |
keySelector = Utils.CreateLambda(keySelector); | |
elementSelector = Utils.CreateLambda(elementSelector); | |
var obj = {}; | |
this.ForEach(function (x) { | |
obj[keySelector(x)] = elementSelector(x); | |
}); | |
return obj; | |
}, | |
// Overload:function(keySelector, elementSelector) | |
// Overload:function(keySelector, elementSelector, compareSelector) | |
ToDictionary: function (keySelector, elementSelector, compareSelector) { | |
keySelector = Utils.CreateLambda(keySelector); | |
elementSelector = Utils.CreateLambda(elementSelector); | |
compareSelector = Utils.CreateLambda(compareSelector); | |
var dict = new Dictionary(compareSelector); | |
this.ForEach(function (x) { | |
dict.Add(keySelector(x), elementSelector(x)); | |
}); | |
return dict; | |
}, | |
// Overload:function() | |
// Overload:function(replacer) | |
// Overload:function(replacer, space) | |
ToJSON: function (replacer, space) { | |
return JSON.stringify(this.ToArray(), replacer, space); | |
}, | |
// Overload:function() | |
// Overload:function(separator) | |
// Overload:function(separator,selector) | |
ToString: function (separator, selector) { | |
if (separator == null) separator = ""; | |
if (selector == null) selector = Functions.Identity; | |
return this.Select(selector).ToArray().join(separator); | |
}, | |
/* Action Methods */ | |
// Overload:function(action<element>) | |
// Overload:function(action<element,index>) | |
Do: function (action) { | |
var source = this; | |
action = Utils.CreateLambda(action); | |
return new Enumerable(function () { | |
var enumerator; | |
var index = 0; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
if (enumerator.MoveNext()) { | |
action(enumerator.Current(), index++); | |
return this.Yield(enumerator.Current()); | |
} | |
return false; | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
// Overload:function(action<element>) | |
// Overload:function(action<element,index>) | |
// Overload:function(func<element,bool>) | |
// Overload:function(func<element,index,bool>) | |
ForEach: function (action) { | |
action = Utils.CreateLambda(action); | |
var index = 0; | |
var enumerator = this.GetEnumerator(); | |
try { | |
while (enumerator.MoveNext()) { | |
if (action(enumerator.Current(), index++) === false) break; | |
} | |
} | |
finally { Utils.Dispose(enumerator); } | |
}, | |
// Overload:function() | |
// Overload:function(separator) | |
// Overload:function(separator,selector) | |
Write: function (separator, selector) { | |
if (separator == null) separator = ""; | |
selector = Utils.CreateLambda(selector); | |
var isFirst = true; | |
this.ForEach(function (item) { | |
if (isFirst) isFirst = false; | |
else document.write(separator); | |
document.write(selector(item)); | |
}); | |
}, | |
// Overload:function() | |
// Overload:function(selector) | |
WriteLine: function (selector) { | |
selector = Utils.CreateLambda(selector); | |
this.ForEach(function (item) { | |
document.write(selector(item)); | |
document.write("<br />"); | |
}); | |
}, | |
Force: function () { | |
var enumerator = this.GetEnumerator(); | |
try { while (enumerator.MoveNext()) { } } | |
finally { Utils.Dispose(enumerator); } | |
}, | |
/* Functional Methods */ | |
Let: function (func) { | |
func = Utils.CreateLambda(func); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { | |
enumerator = Enumerable.From(func(source)).GetEnumerator(); | |
}, | |
function () { | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { Utils.Dispose(enumerator); }) | |
}); | |
}, | |
Share: function () { | |
var source = this; | |
var sharedEnumerator; | |
return new Enumerable(function () { | |
return new IEnumerator( | |
function () { | |
if (sharedEnumerator == null) { | |
sharedEnumerator = source.GetEnumerator(); | |
} | |
}, | |
function () { | |
return (sharedEnumerator.MoveNext()) | |
? this.Yield(sharedEnumerator.Current()) | |
: false; | |
}, | |
Functions.Blank | |
) | |
}); | |
}, | |
MemoizeAll: function () { | |
var source = this; | |
var cache; | |
var enumerator; | |
return new Enumerable(function () { | |
var index = -1; | |
return new IEnumerator( | |
function () { | |
if (enumerator == null) { | |
enumerator = source.GetEnumerator(); | |
cache = []; | |
} | |
}, | |
function () { | |
index++; | |
if (cache.length <= index) { | |
return (enumerator.MoveNext()) | |
? this.Yield(cache[index] = enumerator.Current()) | |
: false; | |
} | |
return this.Yield(cache[index]); | |
}, | |
Functions.Blank | |
) | |
}); | |
}, | |
/* Error Handling Methods */ | |
Catch: function (handler) { | |
handler = Utils.CreateLambda(handler); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
try { | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
} | |
catch (e) { | |
handler(e); | |
return false; | |
} | |
}, | |
function () { Utils.Dispose(enumerator); }); | |
}); | |
}, | |
Finally: function (finallyAction) { | |
finallyAction = Utils.CreateLambda(finallyAction); | |
var source = this; | |
return new Enumerable(function () { | |
var enumerator; | |
return new IEnumerator( | |
function () { enumerator = source.GetEnumerator(); }, | |
function () { | |
return (enumerator.MoveNext()) | |
? this.Yield(enumerator.Current()) | |
: false; | |
}, | |
function () { | |
try { Utils.Dispose(enumerator); } | |
finally { finallyAction(); } | |
}); | |
}); | |
}, | |
/* For Debug Methods */ | |
// Overload:function() | |
// Overload:function(message) | |
// Overload:function(message,selector) | |
Trace: function (message, selector) { | |
if (message == null) message = "Trace"; | |
selector = Utils.CreateLambda(selector); | |
return this.Do(function (item) { | |
console.log(message, ":", selector(item)); | |
}); | |
}, | |
Add: function (obj) { | |
if (this.source == null) { | |
this.source = new Array(); | |
} | |
if (obj instanceof Array) { | |
this.source = this.source.concat(obj); | |
} else { | |
this.source.push(obj); | |
} | |
}, | |
Delete: function (predicate) { | |
if (this.source == null) { | |
return; | |
} | |
predicate = Utils.CreateLambda(predicate); | |
var enumerator = this.GetEnumerator(); | |
var indexes = new Array(); | |
var index = 0; | |
while (enumerator.MoveNext()) { | |
if (predicate(enumerator.Current(), null)) { | |
indexes.push(index); | |
} | |
index++; | |
} | |
for (id in indexes) { | |
var x = id; | |
if (id > 0) | |
x -= id; | |
this.source.splice(indexes[x], 1); | |
} | |
}, | |
Update: function (predicate, obj) { | |
if (this.source == null) | |
return; | |
predicate = Utils.CreateLambda(predicate); | |
var enumerator = this.GetEnumerator(); | |
var indexes = new Array(); | |
var index = 0; | |
while (enumerator.MoveNext()) { | |
if (predicate(enumerator.Current(), null)) { | |
indexes.push(index); | |
} | |
index++; | |
} | |
if (indexes.length == 0) | |
return; | |
if (typeof(obj) == "function") { | |
for (id in indexes) { | |
this.source[indexes[id]] = obj(this.source[indexes[id]]); | |
} | |
} else { | |
for (id in indexes) { | |
this.source[indexes[id]] = obj; | |
} | |
} | |
} | |
} | |
// private | |
// static functions | |
var Functions = | |
{ | |
Identity: function (x) { return x; }, | |
True: function () { return true; }, | |
Blank: function () { } | |
} | |
// static const | |
var Types = | |
{ | |
Boolean: typeof true, | |
Number: typeof 0, | |
String: typeof "", | |
Object: typeof {}, | |
Undefined: typeof undefined, | |
Function: typeof function () { } | |
} | |
// static utility methods | |
var Utils = | |
{ | |
// Create anonymous function from lambda expression string | |
CreateLambda: function (expression) { | |
if (expression == null) return Functions.Identity; | |
if (typeof expression == Types.String) { | |
if (expression == "") { | |
return Functions.Identity; | |
} | |
else if (expression.indexOf("=>") == -1) { | |
return new Function("$,$$,$$$,$$$$", "return " + expression); | |
} | |
else { | |
var expr = expression.match(/^[(\s]*([^()]*?)[)\s]*=>(.*)/); | |
return new Function(expr[1], "return " + expr[2]); | |
} | |
} | |
return expression; | |
}, | |
IsIEnumerable: function (obj) { | |
if (typeof Enumerator != Types.Undefined) { | |
try { | |
new Enumerator(obj); | |
return true; | |
} | |
catch (e) { } | |
} | |
return false; | |
}, | |
Compare: function (a, b) { | |
return (a === b) ? 0 | |
: (a > b) ? 1 | |
: -1; | |
}, | |
Dispose: function (obj) { | |
if (obj != null) obj.Dispose(); | |
} | |
} | |
// IEnumerator State | |
var State = { Before: 0, Running: 1, After: 2 } | |
// name "Enumerator" is conflict JScript's "Enumerator" | |
var IEnumerator = function (initialize, tryGetNext, dispose) { | |
var yielder = new Yielder(); | |
var state = State.Before; | |
this.Current = yielder.Current; | |
this.MoveNext = function () { | |
try { | |
switch (state) { | |
case State.Before: | |
state = State.Running; | |
initialize(); // fall through | |
case State.Running: | |
if (tryGetNext.apply(yielder)) { | |
return true; | |
} | |
else { | |
this.Dispose(); | |
return false; | |
} | |
case State.After: | |
return false; | |
} | |
} | |
catch (e) { | |
this.Dispose(); | |
throw e; | |
} | |
} | |
this.Dispose = function () { | |
if (state != State.Running) return; | |
try { dispose(); } | |
finally { state = State.After; } | |
} | |
} | |
// for tryGetNext | |
var Yielder = function () { | |
var current = null; | |
this.Current = function () { return current; } | |
this.Yield = function (value) { | |
current = value; | |
return true; | |
} | |
} | |
// for OrderBy/ThenBy | |
var OrderedEnumerable = function (source, keySelector, descending, parent) { | |
this.source = source; | |
this.keySelector = Utils.CreateLambda(keySelector); | |
this.descending = descending; | |
this.parent = parent; | |
} | |
OrderedEnumerable.prototype = new Enumerable(); | |
OrderedEnumerable.prototype.CreateOrderedEnumerable = function (keySelector, descending) { | |
return new OrderedEnumerable(this.source, keySelector, descending, this); | |
} | |
OrderedEnumerable.prototype.ThenBy = function (keySelector) { | |
return this.CreateOrderedEnumerable(keySelector, false); | |
} | |
OrderedEnumerable.prototype.ThenByDescending = function (keySelector) { | |
return this.CreateOrderedEnumerable(keySelector, true); | |
} | |
OrderedEnumerable.prototype.GetEnumerator = function () { | |
var self = this; | |
var buffer; | |
var indexes; | |
var index = 0; | |
return new IEnumerator( | |
function () { | |
buffer = []; | |
indexes = []; | |
self.source.ForEach(function (item, index) { | |
buffer.push(item); | |
indexes.push(index); | |
}); | |
var sortContext = SortContext.Create(self, null); | |
sortContext.GenerateKeys(buffer); | |
indexes.sort(function (a, b) { return sortContext.Compare(a, b); }); | |
}, | |
function () { | |
return (index < indexes.length) | |
? this.Yield(buffer[indexes[index++]]) | |
: false; | |
}, | |
Functions.Blank | |
) | |
} | |
OrderedEnumerable.prototype.UpdateItem = function (index, item) { | |
this.source[index] = item; | |
} | |
var SortContext = function (keySelector, descending, child) { | |
this.keySelector = keySelector; | |
this.descending = descending; | |
this.child = child; | |
this.keys = null; | |
} | |
SortContext.Create = function (orderedEnumerable, currentContext) { | |
var context = new SortContext(orderedEnumerable.keySelector, orderedEnumerable.descending, currentContext); | |
if (orderedEnumerable.parent != null) return SortContext.Create(orderedEnumerable.parent, context); | |
return context; | |
} | |
SortContext.prototype.GenerateKeys = function (source) { | |
var len = source.length; | |
var keySelector = this.keySelector; | |
var keys = new Array(len); | |
for (var i = 0; i < len; i++) keys[i] = keySelector(source[i]); | |
this.keys = keys; | |
if (this.child != null) this.child.GenerateKeys(source); | |
} | |
SortContext.prototype.Compare = function (index1, index2) { | |
var comparison = Utils.Compare(this.keys[index1], this.keys[index2]); | |
if (comparison == 0) { | |
if (this.child != null) return this.child.Compare(index1, index2) | |
comparison = Utils.Compare(index1, index2); | |
} | |
return (this.descending) ? -comparison : comparison; | |
} | |
// optimize array or arraylike object | |
var ArrayEnumerable = function (source) { | |
this.source = source; | |
} | |
ArrayEnumerable.prototype = new Enumerable(); | |
ArrayEnumerable.prototype.Any = function (predicate) { | |
return (predicate == null) | |
? (this.source.length > 0) | |
: Enumerable.prototype.Any.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.Count = function (predicate) { | |
return (predicate == null) | |
? this.source.length | |
: Enumerable.prototype.Count.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.ElementAt = function (index) { | |
return (0 <= index && index < this.source.length) | |
? this.source[index] | |
: Enumerable.prototype.ElementAt.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.ElementAtOrDefault = function (index, defaultValue) { | |
return (0 <= index && index < this.source.length) | |
? this.source[index] | |
: defaultValue; | |
} | |
ArrayEnumerable.prototype.First = function (predicate) { | |
return (predicate == null && this.source.length > 0) | |
? this.source[0] | |
: Enumerable.prototype.First.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.FirstOrDefault = function (defaultValue, predicate) { | |
if (predicate != null) { | |
return Enumerable.prototype.FirstOrDefault.apply(this, arguments); | |
} | |
return this.source.length > 0 ? this.source[0] : defaultValue; | |
} | |
ArrayEnumerable.prototype.Last = function (predicate) { | |
return (predicate == null && this.source.length > 0) | |
? this.source[this.source.length - 1] | |
: Enumerable.prototype.Last.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.LastOrDefault = function (defaultValue, predicate) { | |
if (predicate != null) { | |
return Enumerable.prototype.LastOrDefault.apply(this, arguments); | |
} | |
return this.source.length > 0 ? this.source[this.source.length - 1] : defaultValue; | |
} | |
ArrayEnumerable.prototype.Skip = function (count) { | |
var source = this.source; | |
return new Enumerable(function () { | |
var index; | |
return new IEnumerator( | |
function () { index = (count < 0) ? 0 : count }, | |
function () { | |
return (index < source.length) | |
? this.Yield(source[index++]) | |
: false; | |
}, | |
Functions.Blank); | |
}); | |
}; | |
ArrayEnumerable.prototype.TakeExceptLast = function (count) { | |
if (count == null) count = 1; | |
return this.Take(this.source.length - count); | |
} | |
ArrayEnumerable.prototype.TakeFromLast = function (count) { | |
return this.Skip(this.source.length - count); | |
} | |
ArrayEnumerable.prototype.Reverse = function () { | |
var source = this.source; | |
return new Enumerable(function () { | |
var index; | |
return new IEnumerator( | |
function () { | |
index = source.length; | |
}, | |
function () { | |
return (index > 0) | |
? this.Yield(source[--index]) | |
: false; | |
}, | |
Functions.Blank) | |
}); | |
} | |
ArrayEnumerable.prototype.SequenceEqual = function (second, compareSelector) { | |
if ((second instanceof ArrayEnumerable || second instanceof Array) | |
&& compareSelector == null | |
&& Enumerable.From(second).Count() != this.Count()) { | |
return false; | |
} | |
return Enumerable.prototype.SequenceEqual.apply(this, arguments); | |
} | |
ArrayEnumerable.prototype.ToString = function (separator, selector) { | |
if (selector != null || !(this.source instanceof Array)) { | |
return Enumerable.prototype.ToString.apply(this, arguments); | |
} | |
if (separator == null) separator = ""; | |
return this.source.join(separator); | |
} | |
ArrayEnumerable.prototype.GetEnumerator = function () { | |
var source = this.source; | |
var index = 0; | |
return new IEnumerator( | |
Functions.Blank, | |
function () { | |
return (index < source.length) | |
? this.Yield(source[index++]) | |
: false; | |
}, | |
Functions.Blank); | |
} | |
ArrayEnumerable.prototype.UpdateItem = function(index, item) { | |
this.source[index] = item; | |
} | |
// Collections | |
var Dictionary = (function () { | |
// static utility methods | |
var HasOwnProperty = function (target, key) { | |
return Object.prototype.hasOwnProperty.call(target, key); | |
} | |
var ComputeHashCode = function (obj) { | |
if (obj === null) return "null"; | |
if (obj === undefined) return "undefined"; | |
return (typeof obj.toString === Types.Function) | |
? obj.toString() | |
: Object.prototype.toString.call(obj); | |
} | |
// LinkedList for Dictionary | |
var HashEntry = function (key, value) { | |
this.Key = key; | |
this.Value = value; | |
this.Prev = null; | |
this.Next = null; | |
} | |
var EntryList = function () { | |
this.First = null; | |
this.Last = null; | |
} | |
EntryList.prototype = | |
{ | |
AddLast: function (entry) { | |
if (this.Last != null) { | |
this.Last.Next = entry; | |
entry.Prev = this.Last; | |
this.Last = entry; | |
} | |
else this.First = this.Last = entry; | |
}, | |
Replace: function (entry, newEntry) { | |
if (entry.Prev != null) { | |
entry.Prev.Next = newEntry; | |
newEntry.Prev = entry.Prev; | |
} | |
else this.First = newEntry; | |
if (entry.Next != null) { | |
entry.Next.Prev = newEntry; | |
newEntry.Next = entry.Next; | |
} | |
else this.Last = newEntry; | |
}, | |
Remove: function (entry) { | |
if (entry.Prev != null) entry.Prev.Next = entry.Next; | |
else this.First = entry.Next; | |
if (entry.Next != null) entry.Next.Prev = entry.Prev; | |
else this.Last = entry.Prev; | |
} | |
} | |
// Overload:function() | |
// Overload:function(compareSelector) | |
var Dictionary = function (compareSelector) { | |
this.count = 0; | |
this.entryList = new EntryList(); | |
this.buckets = {}; // as Dictionary<string,List<object>> | |
this.compareSelector = (compareSelector == null) ? Functions.Identity : compareSelector; | |
} | |
Dictionary.prototype = | |
{ | |
Add: function (key, value) { | |
var compareKey = this.compareSelector(key); | |
var hash = ComputeHashCode(compareKey); | |
var entry = new HashEntry(key, value); | |
if (HasOwnProperty(this.buckets, hash)) { | |
var array = this.buckets[hash]; | |
for (var i = 0; i < array.length; i++) { | |
if (this.compareSelector(array[i].Key) === compareKey) { | |
this.entryList.Replace(array[i], entry); | |
array[i] = entry; | |
return; | |
} | |
} | |
array.push(entry); | |
} | |
else { | |
this.buckets[hash] = [entry]; | |
} | |
this.count++; | |
this.entryList.AddLast(entry); | |
}, | |
Get: function (key) { | |
var compareKey = this.compareSelector(key); | |
var hash = ComputeHashCode(compareKey); | |
if (!HasOwnProperty(this.buckets, hash)) return undefined; | |
var array = this.buckets[hash]; | |
for (var i = 0; i < array.length; i++) { | |
var entry = array[i]; | |
if (this.compareSelector(entry.Key) === compareKey) return entry.Value; | |
} | |
return undefined; | |
}, | |
Set: function (key, value) { | |
var compareKey = this.compareSelector(key); | |
var hash = ComputeHashCode(compareKey); | |
if (HasOwnProperty(this.buckets, hash)) { | |
var array = this.buckets[hash]; | |
for (var i = 0; i < array.length; i++) { | |
if (this.compareSelector(array[i].Key) === compareKey) { | |
var newEntry = new HashEntry(key, value); | |
this.entryList.Replace(array[i], newEntry); | |
array[i] = newEntry; | |
return true; | |
} | |
} | |
} | |
return false; | |
}, | |
Contains: function (key) { | |
var compareKey = this.compareSelector(key); | |
var hash = ComputeHashCode(compareKey); | |
if (!HasOwnProperty(this.buckets, hash)) return false; | |
var array = this.buckets[hash]; | |
for (var i = 0; i < array.length; i++) { | |
if (this.compareSelector(array[i].Key) === compareKey) return true; | |
} | |
return false; | |
}, | |
Clear: function () { | |
this.count = 0; | |
this.buckets = {}; | |
this.entryList = new EntryList(); | |
}, | |
Remove: function (key) { | |
var compareKey = this.compareSelector(key); | |
var hash = ComputeHashCode(compareKey); | |
if (!HasOwnProperty(this.buckets, hash)) return; | |
var array = this.buckets[hash]; | |
for (var i = 0; i < array.length; i++) { | |
if (this.compareSelector(array[i].Key) === compareKey) { | |
this.entryList.Remove(array[i]); | |
array.splice(i, 1); | |
if (array.length == 0) delete this.buckets[hash]; | |
this.count--; | |
return; | |
} | |
} | |
}, | |
Count: function () { | |
return this.count; | |
}, | |
ToEnumerable: function () { | |
var self = this; | |
return new Enumerable(function () { | |
var currentEntry; | |
return new IEnumerator( | |
function () { currentEntry = self.entryList.First }, | |
function () { | |
if (currentEntry != null) { | |
var result = { Key: currentEntry.Key, Value: currentEntry.Value }; | |
currentEntry = currentEntry.Next; | |
return this.Yield(result); | |
} | |
return false; | |
}, | |
Functions.Blank); | |
}); | |
} | |
} | |
return Dictionary; | |
})(); | |
// dictionary = Dictionary<TKey, TValue[]> | |
var Lookup = function (dictionary) { | |
this.Count = function () { | |
return dictionary.Count(); | |
} | |
this.Get = function (key) { | |
return Enumerable.From(dictionary.Get(key)); | |
} | |
this.Contains = function (key) { | |
return dictionary.Contains(key); | |
} | |
this.ToEnumerable = function () { | |
return dictionary.ToEnumerable().Select(function (kvp) { | |
return new Grouping(kvp.Key, kvp.Value); | |
}); | |
} | |
} | |
var Grouping = function (key, elements) { | |
this.Key = function () { | |
return key; | |
} | |
ArrayEnumerable.call(this, elements); | |
} | |
Grouping.prototype = new ArrayEnumerable(); | |
// out to global | |
return Enumerable; | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment