2733 lines
87 KiB
JavaScript
2733 lines
87 KiB
JavaScript
/*--------------------------------------------------------------------------
|
|
* linq.js - LINQ for JavaScript
|
|
* ver 2.2.0.2 (Jan. 21th, 2011)
|
|
*
|
|
* created and maintained by neuecc <ils@neue.cc>
|
|
* 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();
|
|
}
|
|
if (obj instanceof Enumerable)
|
|
{
|
|
return obj;
|
|
}
|
|
if (typeof obj == Types.Number || typeof obj == Types.Boolean)
|
|
{
|
|
return Enumerable.Repeat(obj, 1);
|
|
}
|
|
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));
|
|
});
|
|
}
|
|
}
|
|
|
|
// 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
|
|
)
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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;
|
|
})() |