Skip to content

Commit

Permalink
Closes #24 Implemented ChunkBy
Browse files Browse the repository at this point in the history
  • Loading branch information
ENikS committed Jul 23, 2016
1 parent dfb9b41 commit 43520f2
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 31 deletions.
15 changes: 14 additions & 1 deletion lib/enumerable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,25 @@ export interface Enumerable<T> extends Iterable<T>, IEnumerable<T> {
*/
Average(func?: (x: T) => number): number;


/**
* Casts the elements of an Iterable to the specified type.
*/
Cast<V>(): Enumerable<V>;

/**
* Group results by contiguous keys.
* @param keySelect A function to extract a key from an element.
* @param elementSelector A transform function to produce a result element
* value from each element.
* @param resultSelector A function to transform the final accumulator value
* into the result value.
* @example
* ChunkBy(a=> a.name);
*/
ChunkBy<K, E, V>(keySelect: (x: T) => K,
elementSelector?: (y: T) => E,
resultSelector?: (a: K, b: Iterable<E>) => V): Enumerable<V>;

/** Concatenates two sequences.
* @param second The sequence to concatenate to the first sequence.
* @example
Expand Down
23 changes: 23 additions & 0 deletions lib/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@ export function* DefaultIfEmpty<T>(target: Iterable<T>, defaultValue: T) {
}


export function* ChunkBy<T, K, E, V>(target: Iterable<T>, keySelect: (x: T) => K,
elementSelector: (x: T) => E,
resultSelector: (a: K, b: Iterable<E>) => V) {
let key: K, box: Array<E>;
for (let value of target) {
let newKey = keySelect(value);
if (key !== newKey && box) {
yield resultSelector(key, box);
box = undefined;
}
if (!box) {
box = new Array<E>();
}
key = newKey;
box.push(elementSelector(value));
}

if (key && box) {
yield resultSelector(key, box);
}
}


export function* Distinct<T, V>(target: Iterable<T>, keySelector: (x: T) => V) {
let set: Set<V> = new Set<V>();
for (let value of target) {
Expand Down
25 changes: 16 additions & 9 deletions lib/linq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,29 +511,36 @@ class EnumerableImpl<T> implements Enumerable<T>, Iterable<T>, IEnumerable<T> {
}


public ChunkBy<K, E, V>(keySelect: (x: T) => K,
elementSelector: (x: T) => E = Constant.selfFn,
resultSelector: (a: K, b: Iterable<E>) => V = (a, b) => b as any):
Enumerable<V> {
return new EnumerableImpl<V>(undefined, Generator.ChunkBy,
[this, keySelect, elementSelector, resultSelector]);
}



public Distinct<V>(keySelector?: (x: T) => V): Enumerable<T> {
if (keySelector)
return new EnumerableImpl<T>(undefined, Generator.Distinct, [this, keySelector]);
return new EnumerableImpl<T>(undefined, Generator.DistinctFast, [this]);
}


public Except<K>(other: Iterable<T>, keySelector?: (x: T) => K):
Enumerable<T> {
public Except<K>(other: Iterable<T>, keySelector?: (x: T) => K): Enumerable<T> {
return new EnumerableImpl<T>(undefined, Generator.Intersect, [ this,
Constant.getKeys(other, keySelector),
true, keySelector ]);
}


public GroupBy<K, E, R>(selKey: (x: T) => K,
public GroupBy<K, E, R>(selKey: (x: T) => K,
selElement: (x: T) => E = Constant.selfFn,
selResult: (a: K, b: Iterable<E>) =>
R = Constant.defGrouping):
Enumerable<R> {
let map: Map<K, Array<E>> = Constant.getKeyedMap(this,
selKey,
selElement);
selResult: (a: K, b: Iterable<E>) =>
R = Constant.defGrouping): Enumerable<R> {
debugger
let map: Map<K, Array<E>> = Constant.getKeyedMap(this, selKey, selElement);
return new EnumerableImpl<R>(undefined, Generator.GroupBy, [map, selResult]);
}

Expand Down
14 changes: 8 additions & 6 deletions lib/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ export var selfFn = (o: any) => o;

/** Default Grouping */
export var defGrouping = (a: any, b: any) => {
if (CONST_UNDEFINED != typeof b[CONST_KEY]) throw CONST_DUPLICATE;
b[CONST_KEY] = a;
if (!b[CONST_KEY]) {
b[CONST_KEY] = a;
}
return b;
};


/** Returns default value for the type */
export function getDefaultVal(type: any, value: any = undefined): any {
if (typeof type !== CONST_STRING) throw new TypeError(CONST_NO_STRING);
Expand All @@ -52,9 +54,9 @@ export function getKeyedMap<T, K, E>(iterable: Iterable<T>, keySelector: (i: T)
let map = new Map<K, Array<E>>();
for (let value of iterable) {
let key = keySelector(value);
if (CONST_UNDEFINED === typeof key) throw CONST_INVALID_KEY;
if (!key) throw CONST_INVALID_KEY;
let group: Array<E> = map.get(key);
if (CONST_UNDEFINED === typeof group) {
if (!group) {
group = [];
map.set(key, group);
}
Expand All @@ -67,9 +69,9 @@ export function getKeyedMapFast<T, K>(iterable: Iterable<T>, keySelector: (x: T)
let map = new Map<K, Array<T>>();
for (let value of iterable) {
let key = keySelector(value);
if (CONST_UNDEFINED === typeof key) throw CONST_INVALID_KEY;
if (!key) throw CONST_INVALID_KEY;
let group: Array<T> = map.get(key);
if (CONST_UNDEFINED === typeof group) {
if (!group) {
group = [];
map.set(key, group);
}
Expand Down
12 changes: 12 additions & 0 deletions test/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,17 @@ export var mix = [
function(){}
];

export var phrase = [
{ key: "A", value: "We" },
{ key: "A", value: "think" },
{ key: "A", value: "that" },
{ key: "B", value: "Linq" },
{ key: "C", value: "is" },
{ key: "A", value: "really" },
{ key: "B", value: "cool" },
{ key: "B", value: "!" }
]



/** Copyright (c) ENikS. All rights reserved. */
34 changes: 33 additions & 1 deletion test/deferred.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
// License for the specific language governing permissions and limitations
// under the License.

import {simpleArray, oddArray, jsn, un1, un2, people, pets, mix} from "./data";
import {
simpleArray, oddArray, jsn, un1, un2, people, pets, mix, phrase
} from "./data";
import {assert} from "chai";
import Linq from "../lib/linq";

Expand All @@ -32,6 +34,36 @@ describe('Deferred Execution -', function () {



// ChunkBy

it('ChunkBy()', function () {

let iterable = Linq(phrase).ChunkBy(o => o.key, o => o.value);

var iterator = iterable[Symbol.iterator]()
var arr = iterator.next().value as Array<string>;
assert.equal(arr.length, 3);
assert.equal(arr[0], "We");
assert.equal(arr[1], "think");
assert.equal(arr[2], "that");
arr = iterator.next().value as Array<string>;
assert.equal(arr.length, 1);
assert.equal(arr[0], "Linq");
arr = iterator.next().value as Array<string>;
assert.equal(arr.length, 1);
assert.equal(arr[0], "is");
arr = iterator.next().value as Array<string>;
assert.equal(arr.length, 1);
assert.equal(arr[0], "really");
arr = iterator.next().value as Array<string>;
assert.equal(arr.length, 2);
assert.equal(arr[0], "cool");
assert.equal(arr[1], "!");
assert.isTrue(iterator.next().done);
});



// Concat

it('Concat()', function () {
Expand Down
Loading

0 comments on commit 43520f2

Please sign in to comment.