From 65f1e2ab6f443f9cc764b9e1bd45f62646068cc6 Mon Sep 17 00:00:00 2001 From: Eugene Sadovoi Date: Fri, 22 Jul 2016 18:38:01 -0400 Subject: [PATCH] Fixes #23 --- .vsnodetools.njsproj | 3 + lib/generators.ts | 9 +- lib/linq.ts | 117 ++++++++---------- test/deferred.ts | 50 +++----- test/module.ts | 4 +- test/reentrancy.ts | 282 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 360 insertions(+), 105 deletions(-) create mode 100644 test/reentrancy.ts diff --git a/.vsnodetools.njsproj b/.vsnodetools.njsproj index 82adab3..470b732 100644 --- a/.vsnodetools.njsproj +++ b/.vsnodetools.njsproj @@ -61,6 +61,9 @@ Mocha + + Mocha + diff --git a/lib/generators.ts b/lib/generators.ts index 8035123..60d053b 100644 --- a/lib/generators.ts +++ b/lib/generators.ts @@ -18,6 +18,7 @@ export function* Forward(target: Array) { yield* target; } + export function* Reverse(target: Array) { for (let i = target.length - 1; i >= 0; i--) { yield target[i]; @@ -39,7 +40,6 @@ export function* DefaultIfEmpty(target: Iterable, defaultValue: T) { if (result.done) { yield defaultValue; } else { - yield result.value; yield* target; } } @@ -224,10 +224,9 @@ export function* SelectMany(target: Iterable, selector: (x: T, i: nu } -export function* SelectManyFast(target: Iterable>) { - for (let subIterable of target) { - yield* subIterable; - } +export function* Concat(target: Iterable, second: Iterable) { + yield* target; + yield* second; } diff --git a/lib/linq.ts b/lib/linq.ts index c42b0cf..594643c 100644 --- a/lib/linq.ts +++ b/lib/linq.ts @@ -28,7 +28,7 @@ import {Enumerable, IEnumerable, IEnumerator} from "./enumerable"; * Converts any Iterable object into LINQ-able object * @param TSource An Array, Map, Set, String or other Iterable object. */ -function getEnumerable(TSource: Iterable | IEnumerable = null): +function getEnumerable(TSource: Iterable | IEnumerable): Enumerable { return new EnumerableImpl(TSource); } @@ -43,7 +43,7 @@ function getEnumerable(TSource: Iterable | IEnumerable = null): * var sum = Range(0, 7).Sum(); */ function getRange(start: number, count: number): Enumerable { - return new EnumerableImpl(Generator.Range(start, count)); + return new EnumerableImpl(undefined, Generator.Range, [start, count]); } @@ -55,7 +55,7 @@ function getRange(start: number, count: number): Enumerable { * var sum = Repeat("v", 7); */ function getRepeat(value: T, count: number): Enumerable { - return new EnumerableImpl(Generator.Repeat(value, count)); + return new EnumerableImpl(undefined, Generator.Repeat, [value, count]); } @@ -96,8 +96,10 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { protected _factoryArg: any; - constructor(target: Iterable | IEnumerable, - factory?: Function, arg?: any) { + constructor(target: Iterable | IEnumerable, + factory?: Function, + arg?: any) { + this._target = >target; this._factory = factory; this._factoryArg = arg; @@ -155,7 +157,7 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { /** Returns JavaScript iterator */ public [Symbol.iterator](): Iterator { - return (null != this._factory) ? this._factory(this._factoryArg) + return (null != this._factory) ? this._factory.apply(this, this._factoryArg) : this._target[Symbol.iterator](); } @@ -175,8 +177,7 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { resultSelector: (aggr: T) => B): B; public Aggregate(seed: A, func: (aggr: A, x: T) => A = Constant.selfFn, - resultSelector: (aggr: A) => B = Constant.selfFn): - B { + resultSelector: (aggr: A) => B = Constant.selfFn): B { let zero: A; let method: (aggr: A, x: T) => A; let selector: (aggr: A) => B; @@ -233,8 +234,8 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { } - public Contains(value: T, - equal: (a: T, b: T) => boolean = (a, b) => a === b): + public Contains(value: T, equal: (a: T, b: T) => + boolean = (a, b) => a === b): boolean { for (let item of this) { if (equal(item, value)) { @@ -247,14 +248,17 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { public Count(predicate: (x: T) => boolean): number { let count = 0; + let countConst = 'count'; if (predicate) { for (let value of this) { if (predicate(value)) { count++; } } - } else if (undefined != (this._target as any)[Constant.CONST_LENGTH]) { + } else if (this._target && (this._target as any)[Constant.CONST_LENGTH]) { count = (this._target as any)[Constant.CONST_LENGTH]; + } else if (this._target && (this._target as any)[countConst]) { + count = (this._target as any)[countConst]; } else { for (let value of this) { count++; @@ -300,7 +304,8 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { public ElementAt(index: number): T { if (Array.isArray(this._target)) { - if (0 > index || (this._target as any)[Constant.CONST_LENGTH] <= index) { + if (0 > index || + (this._target as any)[Constant.CONST_LENGTH] <= index) { throw Constant.CONST_OUTOFRANGE; } return (this._target as any)[index]; @@ -497,31 +502,27 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { public DefaultIfEmpty(defaultValue: T = undefined): Enumerable { - return new EnumerableImpl(Generator.DefaultIfEmpty(this, - defaultValue)); + return new EnumerableImpl(undefined, Generator.DefaultIfEmpty, [this, defaultValue]); } public Concat(second: Iterable): Enumerable { - this._target = Generator.SelectManyFast([this._target, second]) - return this; + return new EnumerableImpl(undefined, Generator.Concat, [this, second]); } public Distinct(keySelector?: (x: T) => V): Enumerable { - this._target = keySelector ? Generator.Distinct(this._target, - keySelector) - : Generator.DistinctFast(this._target) - return this; + if (keySelector) + return new EnumerableImpl(undefined, Generator.Distinct, [this, keySelector]); + return new EnumerableImpl(undefined, Generator.DistinctFast, [this]); } public Except(other: Iterable, keySelector?: (x: T) => K): Enumerable { - this._target = Generator.Intersect(this._target, + return new EnumerableImpl(undefined, Generator.Intersect, [ this, Constant.getKeys(other, keySelector), - true, keySelector); - return this; + true, keySelector ]); } @@ -530,9 +531,10 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { selResult: (a: K, b: Iterable) => R = Constant.defGrouping): Enumerable { - let map: Map> = Constant.getKeyedMap(this._target, - selKey, selElement); - return new EnumerableImpl(Generator.GroupBy(map, selResult)); + let map: Map> = Constant.getKeyedMap(this, + selKey, + selElement); + return new EnumerableImpl(undefined, Generator.GroupBy, [map, selResult]); } @@ -542,21 +544,20 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { resultSelector: (a: T, b: Iterable) => R = Constant.defGrouping): Enumerable { - return new EnumerableImpl(Generator.GroupJoin(this._target, + return new EnumerableImpl(undefined, Generator.GroupJoin, [this, oKeySelect, resultSelector, Constant.getKeyedMapFast(inner, - iKeySelect))); + iKeySelect)]); } public Intersect(other: Iterable, keySelector?: (x: T) => K): Enumerable { - this._target = Generator.Intersect(this._target, + return new EnumerableImpl(undefined, Generator.Intersect, [this, Constant.getKeys(other, keySelector), - false, keySelector) - return this; + false, keySelector]); } @@ -564,12 +565,8 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { oSelector: (o: T) => K, iSelector: (i: I) => K, transform: (o: T, i: I) => R): Enumerable { - this._target = Generator.Join(this._target, - oSelector, - transform, - Constant.getKeyedMapFast(inner, - iSelector)); - return this as any as Enumerable; + return new EnumerableImpl(undefined, Generator.Join, + [this, oSelector, transform, Constant.getKeyedMapFast(inner, iSelector)]); } @@ -596,8 +593,7 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { typeName = undefined; } - this._target = Generator.OfType(this._target, obj, typeName); - return this; + return new EnumerableImpl(undefined, Generator.OfType, [this, obj, typeName]); } @@ -660,26 +656,25 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { public Range(start: number, count: number): Enumerable { - return new EnumerableImpl(Generator.Range(start, count)); + return new EnumerableImpl(undefined, Generator.Range, [start, count]); } public Repeat(element: V, count: number): Enumerable { - return new EnumerableImpl(Generator.Repeat(element, count)); + return new EnumerableImpl(undefined, Generator.Repeat, [element, count]); } public Reverse(): Enumerable { let array: Array = Array.isArray(this._target) ? >this._target : this.ToArray(); - return new EnumerableImpl(null, () => Generator.Reverse(array)); + return new EnumerableImpl(undefined, Generator.Reverse, [array]); } public Select(transform: (x: T) => V): Enumerable; public Select(transform: (x: T, index: number) => V): Enumerable { - this._target = Generator.Select(this._target, transform) - return this as any as Enumerable; + return new EnumerableImpl(undefined, Generator.Select, [this, transform]); } @@ -688,57 +683,51 @@ class EnumerableImpl implements Enumerable, Iterable, IEnumerable { result: (x: T, s: S) => V = (x, s) => s as any as V): Enumerable { - this._target = Generator.SelectMany(this._target, selector, result); - return this as any as Enumerable; + return new EnumerableImpl(undefined, Generator.SelectMany, + [this, selector, result]); } public Skip(skip: number): Enumerable { - this._target = Generator.Skip(this._target, skip) - return this; + return new EnumerableImpl(undefined, Generator.Skip, [this, skip]); } public SkipWhile(predicate: (x: T) => boolean): Enumerable; public SkipWhile(predicate: (x: T, i: number) => boolean): Enumerable { - this._target = Generator.SkipWhile(this._target, predicate); - return this; + return new EnumerableImpl(undefined, Generator.SkipWhile, [this, predicate]); } public Take(take: number): Enumerable { - this._target = Generator.TakeWhile(this._target, (a: T, n: number) => take > n); - return this; + return new EnumerableImpl(undefined, Generator.TakeWhile, + [this, (a: T, n: number) => take > n]); } public TakeWhile(predicate: (x: T, i: number) => boolean): Enumerable { - this._target = Generator.TakeWhile(this._target, predicate); - return this; + return new EnumerableImpl(undefined, Generator.TakeWhile, [this, predicate]); } public Union(second: Iterable, keySelector?: (x: T) => K): - Enumerable { - this._target = keySelector - ? Generator.Union(this._target, second, keySelector) - : Generator.UnionFast(this._target, second) - return this; + Enumerable { + if (keySelector) + return new EnumerableImpl(undefined, Generator.Union, [this, second, keySelector]); + return new EnumerableImpl(undefined, Generator.UnionFast, [this, second]); } public Where(predicate: (x: T) => Boolean): Enumerable; public Where(predicate: (x: T, i: number) => Boolean = Constant.trueFn): Enumerable { - this._target = Generator.Where(this._target, predicate); - return this; + return new EnumerableImpl(undefined, Generator.Where, [this, predicate]); } public Zip(second: Iterable, func: (a: T, b: V) => Z): - Enumerable { - this._target = Generator.Zip(this._target, second, func); - return this as any as Enumerable + Enumerable { + return new EnumerableImpl(undefined, Generator.Zip, [this, second, func]); } } diff --git a/test/deferred.ts b/test/deferred.ts index 7d8de85..3d434b4 100644 --- a/test/deferred.ts +++ b/test/deferred.ts @@ -32,6 +32,22 @@ describe('Deferred Execution -', function () { + // Concat + + it('Concat()', function () { + var iterable = Linq([0, 1, 2]).Concat([3, 4]); + var iterator = iterable[Symbol.iterator]() + + assert.equal(0, iterator.next().value); + assert.equal(1, iterator.next().value); + assert.equal(2, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(4, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + // Select it('Select()', function () { @@ -55,24 +71,6 @@ describe('Deferred Execution -', function () { - // Iterable - - it('Iterable', function () { - let Iterable = Linq(simpleArray).Where(a => a % 2 == 1); -console.log("----------"); - for (let value of Iterable) { - console.log(value); - } -console.log("----------"); - for (let value of Iterable) { - console.log(value); - } -console.log("----------"); - }); - - - - // Distinct it('Distinct() - Number', function () { @@ -522,22 +520,6 @@ console.log("----------"); assert.isTrue(iterator.next().done); }); - - - // Concat - - it('Concat()', function () { - var iterable = Linq([0, 1, 2]).Concat([3, 4]); - var iterator = iterable[Symbol.iterator]() - - assert.equal(0, iterator.next().value); - assert.equal(1, iterator.next().value); - assert.equal(2, iterator.next().value); - assert.equal(3, iterator.next().value); - assert.equal(4, iterator.next().value); - assert.isTrue(iterator.next().done); - }); - }); /** Copyright (c) ENikS. All rights reserved. */ diff --git a/test/module.ts b/test/module.ts index dc5d50d..73218b4 100644 --- a/test/module.ts +++ b/test/module.ts @@ -42,8 +42,8 @@ describe('Module Interface -', function () { it('Range() - Source', function () { var array = Enumerable.Range(1, 100) - .Where(o => o % 2 == 1) - .ToArray(); + .Where(o => o % 2 == 1) + .ToArray(); assert.equal(array.length, 50); assert.equal(Enumerable.Range(0, 15).Where(o => o % 2 == 1).Count(), 7); }); diff --git a/test/reentrancy.ts b/test/reentrancy.ts new file mode 100644 index 0000000..15b81d6 --- /dev/null +++ b/test/reentrancy.ts @@ -0,0 +1,282 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Licensed under the Apache License, Version 2.0 ( the "License" ); you may +// not use this file except in compliance with the License. You may obtain a +// copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import {jsn, fruits, people, pets, simpleArray, mix} from "./data"; +import {assert} from "chai"; +import {asEnumerable, Range, Repeat} from "../lib/linq"; + + + +describe('Reentrancy -', function () { + + it('Range', function () { + var iterable = Range(0, 2); + var iterator = iterable[Symbol.iterator]() + assert.equal(0, iterator.next().value); + assert.equal(1, iterator.next().value); + assert.isTrue(iterator.next().done); + + iterator = iterable[Symbol.iterator]() + assert.equal(0, iterator.next().value); + assert.equal(1, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + it('Repeat', function () { + var iterable = Repeat(0, 3); + var iterator = iterable[Symbol.iterator]() + assert.equal(0, iterator.next().value); + assert.equal(0, iterator.next().value); + assert.equal(0, iterator.next().value); + assert.isTrue(iterator.next().done); + + iterator = iterable[Symbol.iterator]() + assert.equal(0, iterator.next().value); + assert.equal(0, iterator.next().value); + assert.equal(0, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + +/* + it('DefaultIfEmpty() - Not empty', function () { + var iterable = Range(0, 5).DefaultIfEmpty(0); + var iterator = iterable[Symbol.iterator]() + assert.equal(iterator.next().value, 0); + assert.equal(iterator.next().value, 1); + assert.equal(iterator.next().value, 2); + assert.equal(iterator.next().value, 3); + assert.equal(iterator.next().value, 4); + assert.isTrue(iterator.next().done); + }); + + + it('Select()', function () { + let array = asEnumerable(jsn).Select((a) => a.name).ToArray(); + assert.equal(array.length, 4); + assert.equal('d', array[0]); + assert.equal('c', array[1]); + assert.equal('b', array[2]); + assert.equal('a', array[3]); + }); + + + it('Distinct() - Key', function () { + let test = [ + { id: 1, "name": "d" }, + { id: 1, "name": "c" }, + { id: 3, "name": "b" }, + { id: 4, "name": "a" } + ]; + let iterable = asEnumerable(test).Distinct(o => o.id); + let iterator = iterable[Symbol.iterator]() + assert.equal("d", (iterator.next().value).name); + assert.equal("b", (iterator.next().value).name); + assert.equal("a", (iterator.next().value).name); + assert.isTrue(iterator.next().done); + }); + + + it('Where()', function () { + let iterable = asEnumerable(simpleArray).Where(a => a % 2 == 1); + let iterator = iterable[Symbol.iterator]() + assert.equal(1, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(5, iterator.next().value); + assert.equal(7, iterator.next().value); + assert.equal(9, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Skip()', function () { + let iterable = asEnumerable(simpleArray).Skip(7); + let iterator = iterable[Symbol.iterator]() + assert.equal(8, iterator.next().value); + assert.equal(9, iterator.next().value); + assert.equal(10, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Take()', function () { + var iterable = asEnumerable(simpleArray).Take(3); + var iterator = iterable[Symbol.iterator]() + assert.equal(1, iterator.next().value); + assert.equal(2, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Except()', function () { + var iterable = asEnumerable(simpleArray).Except([0, 2, 4, 6, 11]); + var iterator = iterable[Symbol.iterator]() + assert.equal(1, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(5, iterator.next().value); + assert.equal(7, iterator.next().value); + assert.equal(8, iterator.next().value); + assert.equal(9, iterator.next().value); + assert.equal(10, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Intersect()', function () { + var iterable = asEnumerable(simpleArray).Intersect([1, 3, 5, 11, 23, 44]); + var iterator = iterable[Symbol.iterator]() + assert.equal(1, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(5, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('OfType()', function () { + + var iterable = asEnumerable(mix).OfType(Object); + var iterator = iterable[Symbol.iterator](); + assert.equal(iterator.next().value, 1); + assert.equal(iterator.next().value, mix[3]); + assert.equal(iterator.next().value, mix[4]); + assert.equal(iterator.next().value, mix[5]); + assert.equal(iterator.next().value, mix[10]); + assert.equal(iterator.next().value, mix[11]); + assert.equal(iterator.next().value, mix[12]); + assert.equal(iterator.next().value, mix[13]); + assert.equal(iterator.next().value, mix[14]); + assert.equal(iterator.next().value, mix[15]); + assert.equal(iterator.next().value, mix[17]); + assert.isTrue(iterator.next().done); + }); + + + it('Union()', function () { + var iterable = asEnumerable([0, 1, 2, 3, 4, 5, 6, 7]).Union([5, 6, 7, 8, 9]); + var iterator = iterable[Symbol.iterator]() + assert.equal(0, iterator.next().value); + assert.equal(1, iterator.next().value); + assert.equal(2, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(4, iterator.next().value); + assert.equal(5, iterator.next().value); + assert.equal(6, iterator.next().value); + assert.equal(7, iterator.next().value); + assert.equal(8, iterator.next().value); + assert.equal(9, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Join()', function () { + var iterable = + asEnumerable(people).Join(pets, + person => person, + pet => pet.Owner, + (person, pet) => { + return person.Name + " - " + pet.Name; + }); + var iterator = iterable[Symbol.iterator]() + assert.equal("Hedlund, Magnus - Daisy", iterator.next().value); + assert.equal("Adams, Terry - Barley", iterator.next().value); + assert.equal("Adams, Terry - Boots", iterator.next().value); + assert.equal("Weiss, Charlotte - Whiskers", iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('GroupJoin()', function () { + var iterable = asEnumerable(people) + .GroupJoin(pets, + person => person, + pet => pet.Owner, + (person, petCollection) => { + return { + Owner: person.Name, + Pets: asEnumerable(petCollection) + .Select(pet => pet.Name) + .ToArray() + }; + }); + var iterator = iterable[Symbol.iterator](); + var result = iterator.next().value; + assert.isTrue(Array.isArray(result.Pets)) + assert.equal("Hedlund, Magnus", result.Owner); + assert.equal(1, result.Pets.length); + assert.equal("Daisy", result.Pets[0]); + result = iterator.next().value; + assert.equal("Adams, Terry", result.Owner); + assert.equal(2, result.Pets.length); + assert.equal("Barley", result.Pets[0]); + assert.equal("Boots", result.Pets[1]); + result = iterator.next().value; + assert.equal("Weiss, Charlotte", result.Owner); + assert.equal(1, result.Pets.length); + assert.equal("Whiskers", result.Pets[0]); + assert.isTrue(iterator.next().done); + }); + + + it('GroupBy()', function () { + var iterable: any = asEnumerable(pets).GroupBy(pet => pet.Age); + + var iterator = iterable[Symbol.iterator](); + var result = iterator.next().value; + assert.equal(8, result.key); + assert.equal(1, result.length); + result = iterator.next().value; + assert.equal(4, result.key); + assert.equal(2, result.length); + result = iterator.next().value; + assert.equal(1, result.key); + assert.equal(1, result.length); + assert.isTrue(iterator.next().done); + }); + + + it('SelectMany()', function () { + + var iterable = asEnumerable(jsn).SelectMany(a => a.ids); + var iterator = iterable[Symbol.iterator]() + assert.equal(11, iterator.next().value); + assert.equal(21, iterator.next().value); + assert.equal(31, iterator.next().value); + assert.equal(12, iterator.next().value); + assert.equal(22, iterator.next().value); + assert.equal(32, iterator.next().value); + assert.equal(13, iterator.next().value); + assert.equal(23, iterator.next().value); + assert.equal(33, iterator.next().value); + assert.equal(14, iterator.next().value); + assert.equal(24, iterator.next().value); + assert.equal(34, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + + + it('Concat()', function () { + var iterable = asEnumerable([0, 1, 2]).Concat([3, 4]); + var iterator = iterable[Symbol.iterator]() + + assert.equal(0, iterator.next().value); + assert.equal(1, iterator.next().value); + assert.equal(2, iterator.next().value); + assert.equal(3, iterator.next().value); + assert.equal(4, iterator.next().value); + assert.isTrue(iterator.next().done); + }); + */ +}); + +/** Copyright (c) ENikS. All rights reserved. */