diff --git a/__tests__/list.test.ts b/__tests__/list.test.ts index a139cb1..b34f83b 100644 --- a/__tests__/list.test.ts +++ b/__tests__/list.test.ts @@ -619,6 +619,39 @@ test('OrderByDescending', t => { .ToArray(), [6, 5, 4, 3, 2, 1] ) + + // sorting with custom comparer function + const items = new List([ + new Product({ Name: 'Edward', Code: 21 }), + new Product({ Name: 'Sharpe', Code: 37 }), + new Product({ Name: 'And', Code: 45 }), + new Product({ Name: 'The', Code: -12 }), + new Product({ Name: 'Magnetic', Code: 13 }), + new Product({ Name: 'Zeros', Code: 37 }) + ]) + + const nameComparerFn = (a: IProduct, b: IProduct) => { + const nameA = a.Name.toUpperCase() // ignore upper and lowercase + const nameB = b.Name.toUpperCase() // ignore upper and lowercase + if (nameA < nameB) { + return -1 + } + if (nameA > nameB) { + return 1 + } + + // names must be equal + return 0 + } + const ordered = new List([ + new Product({ Name: 'And', Code: 45 }), + new Product({ Name: 'Edward', Code: 21 }), + new Product({ Name: 'Magnetic', Code: 13 }), + new Product({ Name: 'Sharpe', Code: 37 }), + new Product({ Name: 'The', Code: -12 }), + new Product({ Name: 'Zeros', Code: 37 }) + ]) + t.deepEqual(items.OrderByDescending(a => a, nameComparerFn).ToList(), ordered) }) test('ThenBy', t => { @@ -1074,15 +1107,15 @@ test('ToLookup', t => { t.deepEqual(lookup, result) }) -test.skip('Union', t => { +test('Union', t => { const ints1 = new List([5, 3, 9, 7, 5, 9, 3, 7]) const ints2 = new List([8, 3, 6, 4, 4, 9, 1, 0]) t.deepEqual(ints1.Union(ints2).ToArray(), [5, 3, 9, 7, 8, 6, 4, 1, 0]) const result = [ - { Name: 'apple', Code: 9 }, - { Name: 'orange', Code: 4 }, - { Name: 'lemon', Code: 12 } + new Product({ Name: 'apple', Code: 9 }), + new Product({ Name: 'orange', Code: 4 }), + new Product({ Name: 'lemon', Code: 12 }) ] const store1 = new List([ new Product({ Name: 'apple', Code: 9 }), diff --git a/package.json b/package.json index fb2cbd3..4cc067a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "types": "dist/src/index.d.ts", "scripts": { "build": "tsc", - "check-coverage": "nyc check-coverage --statements 99 --branches 97 --functions 98 --lines 99", + "check-coverage": "nyc check-coverage --statements 100 --branches 98 --functions 99 --lines 100", "commit": "git-cz", "cover": "nyc --require ts-node/register --reporter=lcov npm t", "docs": "typedoc --out ../docs/ src/index.ts -m commonjs -t ES6", diff --git a/src/list.ts b/src/list.ts index 3f3081c..756bff0 100644 --- a/src/list.ts +++ b/src/list.ts @@ -3,15 +3,14 @@ import { composeComparers, negate, isObj, equal, keyComparer } from './helpers' type PredicateType = (value?: T, index?: number, list?: T[]) => boolean class List { - - protected _elements: T[] + protected _elements: T[]; /** * Make the List iterable and Spreadable */ *[Symbol.iterator]() { for (let element of this._elements) { - yield element; + yield element } } @@ -19,14 +18,14 @@ class List { * property represents the Object name */ get [Symbol.toStringTag]() { - return 'List'; // Expected output: "[object List]" + return 'List' // Expected output: "[object List]" } /** * Defaults the elements of the list */ constructor(elements: T[] = []) { - this._elements = [...elements]; + this._elements = [...elements] } /** @@ -60,7 +59,10 @@ class List { /** * Applies an accumulator function over a sequence. */ - public Aggregate(accumulator: (accum: U, value?: T, index?: number, list?: T[]) => any, initialValue?: U): any { + public Aggregate( + accumulator: (accum: U, value?: T, index?: number, list?: T[]) => any, + initialValue?: U + ): any { return this._elements.reduce(accumulator, initialValue) } @@ -84,7 +86,9 @@ class List { * Computes the average of a sequence of number values that are obtained by invoking * a transform function on each element of the input sequence. */ - public Average(transform?: (value?: T, index?: number, list?: T[]) => any): number { + public Average( + transform?: (value?: T, index?: number, list?: T[]) => any + ): number { return this.Sum(transform) / this.Count(transform) } @@ -120,9 +124,7 @@ class List { * Returns the number of elements in a sequence. */ public Count(predicate?: PredicateType): number { - return predicate - ? this.Where(predicate).Count() - : this._elements.length + return predicate ? this.Where(predicate).Count() : this._elements.length } /** @@ -130,9 +132,7 @@ class List { * in a singleton collection if the sequence is empty. */ public DefaultIfEmpty(defaultValue?: T): List { - return this.Count() - ? this - : new List([defaultValue]) + return this.Count() ? this : new List([defaultValue]) } /** @@ -165,7 +165,9 @@ class List { if (index < this.Count() && index >= 0) { return this._elements[index] } - throw new Error('ArgumentOutOfRangeException: index is less than 0 or greater than or equal to the number of elements in source.') + throw new Error( + 'ArgumentOutOfRangeException: index is less than 0 or greater than or equal to the number of elements in source.' + ) } /** @@ -279,7 +281,9 @@ class List { key2: (key: U) => any, result: (first: T, second: U) => R ): List { - return this.SelectMany(x => list.Where(y => key2(y) === key1(x)).Select(z => result(x, z))) + return this.SelectMany(x => + list.Where(y => key2(y) === key1(x)).Select(z => result(x, z)) + ) } /** @@ -304,7 +308,9 @@ class List { /** * Returns the maximum value in a generic sequence. */ - public Max(selector?: (value: T, index: number, array: T[]) => number): number { + public Max( + selector?: (value: T, index: number, array: T[]) => number + ): number { const id = x => x return Math.max(...this._elements.map(selector || id)) } @@ -312,7 +318,9 @@ class List { /** * Returns the minimum value in a generic sequence. */ - public Min(selector?: (value: T, index: number, array: T[]) => number): number { + public Min( + selector?: (value: T, index: number, array: T[]) => number + ): number { const id = x => x return Math.min(...this._elements.map(selector || id)) } @@ -333,7 +341,7 @@ class List { typeName = typeof true break case Function: - typeName = typeof function () { } // tslint:disable-line no-empty + typeName = typeof function() {} // tslint:disable-line no-empty break default: typeName = null @@ -347,7 +355,10 @@ class List { /** * Sorts the elements of a sequence in ascending order according to a key. */ - public OrderBy(keySelector: (key: T) => any, comparer = keyComparer(keySelector, false)): List { + public OrderBy( + keySelector: (key: T) => any, + comparer = keyComparer(keySelector, false) + ): List { // tslint:disable-next-line: no-use-before-declare return new OrderedList(this._elements, comparer) } @@ -355,7 +366,10 @@ class List { /** * Sorts the elements of a sequence in descending order according to a key. */ - public OrderByDescending(keySelector: (key: T) => any, comparer = keyComparer(keySelector, true)): List { + public OrderByDescending( + keySelector: (key: T) => any, + comparer = keyComparer(keySelector, true) + ): List { // tslint:disable-next-line: no-use-before-declare return new OrderedList(this._elements, comparer) } @@ -407,14 +421,18 @@ class List { /** * Projects each element of a sequence into a new form. */ - public Select(selector: (element: T, index: number) => TOut): List { + public Select( + selector: (element: T, index: number) => TOut + ): List { return new List(this._elements.map(selector)) } /** * Projects each element of a sequence to a List and flattens the resulting sequences into one sequence. */ - public SelectMany>(selector: (element: T, index: number) => TOut): TOut { + public SelectMany>( + selector: (element: T, index: number) => TOut + ): TOut { return this.Aggregate( (ac, _, i) => ( ac.AddRange( @@ -450,9 +468,7 @@ class List { * this method throws an exception if there is more than one element in the sequence. */ public SingleOrDefault(predicate?: PredicateType): T { - return this.Count(predicate) - ? this.Single(predicate) - : undefined + return this.Count(predicate) ? this.Single(predicate) : undefined } /** @@ -482,7 +498,9 @@ class List { * Computes the sum of the sequence of number values that are obtained by invoking * a transform function on each element of the input sequence. */ - public Sum(transform?: (value?: T, index?: number, list?: T[]) => number): number { + public Sum( + transform?: (value?: T, index?: number, list?: T[]) => number + ): number { return transform ? this.Select(transform).Sum() : this.Aggregate((ac, v) => (ac += +v), 0) @@ -521,7 +539,10 @@ class List { /** * Creates a Dictionary from a List according to a specified key selector function. */ - public ToDictionary(key: (key: T) => TKey, value?: (value: T) => TValue): List<{ Key: TKey; Value: T | TValue }> { + public ToDictionary( + key: (key: T) => TKey, + value?: (value: T) => TValue + ): List<{ Key: TKey; Value: T | TValue }> { return this.Aggregate((dicc, v, i) => { dicc[ this.Select(key) @@ -547,7 +568,10 @@ class List { /** * Creates a Lookup from an IEnumerable according to specified key selector and element selector functions. */ - public ToLookup(keySelector: (key: T) => string | number, elementSelector: (element: T) => TResult): { [key: string]: TResult[] } { + public ToLookup( + keySelector: (key: T) => string | number, + elementSelector: (element: T) => TResult + ): { [key: string]: TResult[] } { return this.GroupBy(keySelector, elementSelector) } @@ -568,7 +592,10 @@ class List { /** * Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results. */ - public Zip(list: List, result: (first: T, second: U) => TOut): List { + public Zip( + list: List, + result: (first: T, second: U) => TOut + ): List { return list.Count() < this.Count() ? list.Select((x, y) => result(this.ElementAt(y), x)) : this.Select((x, y) => result(x, list.ElementAt(y))) @@ -587,6 +614,15 @@ class OrderedList extends List { this._elements.sort(this._comparer) } + /** + * Allows you to get the parent List out of the OrderedList + * @override + * @returns and ordered list turned into a regular List + */ + public ToList(): List { + return new List(this._elements) + } + /** * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. * @override