projects/wms-framework/src/lib/baseframework/collections.ts
Base class for DistinctIterator, IntersectIterator and ExceptIterator.
This class contains the logic to detect if the elements have an 'Equals()' function, and use it if available to compare elements. If 'Equals()' is not available, then '===' is used to compare elements.
The logic to discriminate which elements are returned must be implemented on the derived classes.
Iterator
Properties |
|
Methods |
|
constructor(first: Iterable<T>, second: Iterable<T>)
|
|||||||||
Parameters :
|
Protected baseSet |
Type : Set<T>
|
Protected baseSetDelete |
Type : Function
|
Protected baseSetHas |
Type : Function
|
Protected iterator |
Type : Iterator<T>
|
Protected nextResult |
Type : IteratorResult<T | any>
|
Abstract beforeReturn |
beforeReturn()
|
Returns :
void
|
next | ||||||
next(_value?: any)
|
||||||
Parameters :
Returns :
IteratorResult<T>
|
Abstract returnCondition |
returnCondition()
|
Returns :
boolean
|
import { INotifyPropertyChanged } from '../basecomponentmodel/INotifyPropertyChanged';
import { supportedInterfacesCompatibilityMetadataKey } from '../decorators/ClassInfo';
import { Debugger } from '../diagnostics/Debugger';
import { CancelEventArgs } from '../helpers/CancelEventArgs';
import { SubscriptionEvent } from '../utils/SubscriptionEvent';
import { InvalidOperationException } from './Exceptions';
import { SortDescription } from './SortDescription';
/**
* IEqualityComparer interface
*
* @export
* @interface IEqualityComparer
* @template T
* @wInterface System.Collections.Generic.IEqualityComparer`1
* @wNetSupport
*/
export interface IEqualityComparer<T> {
Equals(x: T, y: T): boolean;
GetHashCode(x: T): number;
}
/**
* IComparer interface
*
* @export
* @interface IComparer
* @template T
* @wInterface System.Collections.Generic.IComparer`1
* @wNetSupport
*/
export interface IComparer<T> {
Compare(x: T, y: T): number;
}
/**
* IComparable interface
*
* @export
* @interface IComparable
* @template T
* @wInterface System.IComparable`1
* @wNetSupport
*/
export interface IComparable<T> {
CompareTo(x: T): number;
}
/**
* IEquatable interface
*
* @export
* @interface IEquatable
* @template T
* @wInterface System.IEquatable`1
* @wNetSupport
*/
export interface IEquatable<T> {
Equals(other: T): boolean;
}
export interface ISupportCollection<T> {
internalArray: Array<T>;
}
/**
* IOrderedIterable interface
*
* @export
* @interface IOrderedIterable
* @extends {Iterable<V>}
* @template V
* @wInterface System.Linq.IOrderedEnumerable`1
* @wNetSupport
*/
export interface IOrderedIterable<V> extends Iterable<V> {
/**
* Creates ordered iterable
*
* @template K
* @param {(e: V) => K} keySelector
* @param {IComparer<K>} comparer
* @param {boolean} descending
* @returns {IOrderedIterable<V>}
* @memberof IOrderedIterable
* @wMethod CreateOrderedEnumerable
*/
CreateOrderedIterable<K>(
keySelector: (e: V) => K,
comparer: IComparer<K>,
descending: boolean
): IOrderedIterable<V>;
}
export interface UnTypedIterable {}
export interface IUntypedCollection {}
/**
* ICollection interface
*
* @export
* @interface ICollection
* @extends {Iterable<T>}
* @extends {ISupportCollection<T>}
* @template T
* @wInterface System.Collections.Generic.ICollection`1
* @wNetSupport
*/
export interface ICollection<T> extends Iterable<T>, ISupportCollection<T> {
/**
* The number of elemenst in the collection
*
* @type {number}
* @memberof ICollection
* @wProperty Count
*/
count: number;
/**
* Adds a value to the collection
*
* @param {T} value
* @memberof ICollection
* @wMethod Add
*/
add(value: T): void;
/**
* Clears the collection
*
* @memberof ICollection
* @wMethod Clear
*/
clear(): void;
/**
* Check if a element is contained in the collection
*
* @param {T} value
* @returns {boolean}
* @memberof ICollection
* @wMethod Contains
*/
contains(value: T): boolean;
/**
* Removes the element from the collection
*
* @param {T} value
* @returns {boolean}
* @memberof ICollection
*/
remove(value: T): boolean;
copyTo(target: Array<T>, index: number): void;
}
/**
* IPagedCollectionView interface
*
* @export
* @interface IPagedCollectionView
* @wInterface System.ComponentModel.IPagedCollectionView
*/
export interface IPagedCollectionView {
CanChangePage: boolean;
IsPageChanging: boolean;
ItemCount: number;
PageIndex: number;
PageSize: number;
TotalItemCount: number;
PageChanged: SubscriptionEvent<(e: any, args: CollectionChangeInfo) => void>;
PageChanging: SubscriptionEvent<
(e: any, args: PageChangingEventArgs) => void
>;
MoveToFirstPage(): boolean;
MoveToLastPage(): boolean;
MoveToNextPage(): boolean;
MoveToPage(pageIndex: number): boolean;
MoveToPreviousPage(): boolean;
}
/**
* IList interface
*
* @export
* @interface IList
* @extends {ICollection<T>}
* @template T
* @wInterface System.Collections.Generic.IList`1
* @wNetSupport
*/
export interface IList<T> extends ICollection<T> {
getItem(index: number): T;
setItem(index: number, value: T);
/**
* Gets the index of an element in the list
*
* @param {T} value
* @returns {number}
* @memberof IList
* @wMethod IndexOf
*/
indexOf(value: T): number;
/**
* Insert element in position
*
* @param {number} index
* @param {T} value
* @memberof IList
* @wMethod Insert
*/
insert(index: number, value: T);
/**
* Removes element at index
*
* @param {number} index
* @memberof IList
* @wMethod RemoveAt
*/
removeAt(index: number);
/**
* Sorts the list
*
* @memberof IList
* @wMethod Sort
*/
sort();
/**
* Sorts the list by comparer
*
* @param {IComparer<T>} comparer
* @memberof IList
* @wMethod Sort
*/
sort(comparer: IComparer<T>);
}
/**
* SimpleList Class
*
* @export
* @class SimpleList
* @implements {IList}
* @wType System.Collections.Generic.List`1
* @wNetSupport
*/
export class SimpleList<T> implements IList<T> {
protected static interfacesInitialized = false;
protected _internalArray: T[] = [];
protected static initializeSupportedInterfaces(): void {
if (!SimpleList.interfacesInitialized) {
const setMetadataInfo = Reflect.metadata(
supportedInterfacesCompatibilityMetadataKey,
[
'System.Collections.Generic.ICollection`1',
'System.Collections.Generic.IList`1',
'System.Collections.Generic.IList',
'System.Collections.IList',
]
);
setMetadataInfo(SimpleList);
SimpleList.interfacesInitialized = true;
}
}
constructor();
constructor(initialValues: Iterable<T>);
constructor(capacity: number);
constructor(arg?: unknown) {
const newLocal = typeof arg;
// capacity argument still not supported
if (newLocal != 'undefined' && newLocal != 'number') {
const initialValues = arg as Iterable<T>;
this.internalArray = [...initialValues];
}
SimpleList.initializeSupportedInterfaces();
}
/**
* Gets or sets the internal array.
*
* @type {T[]}
* @memberof SimpleList
*/
get internalArray(): T[] {
// eslint-disable-next-line no-underscore-dangle
return this._internalArray;
}
set internalArray(value: T[]) {
// eslint-disable-next-line no-underscore-dangle
this._internalArray = value;
}
getItem(index: number): T {
if (index >= this.internalArray.length || index < 0) {
throw new Error('Invalid index');
}
return this.internalArray[index];
}
setItem(index: number, value: T) {
if (index >= this.internalArray.length || index < 0) {
throw new Error('Invalid index');
}
this.internalArray[index] = value;
}
/**
* Index of value in list
*
* @param {T} value
* @returns
* @memberof SimpleList
* @wMethod IndexOf
*/
indexOf(value: T) {
return this.internalArray.indexOf(value);
}
/**
* Inserts element in index
*
* @param {number} index
* @param {T} value
* @memberof SimpleList
* @wMethod Insert
*/
insert(index: number, value: T) {
this.internalArray.splice(index, 0, value);
}
/**
* Removes element at index
*
* @param {number} index
* @memberof SimpleList
* @wMethod RemoveAt
*/
removeAt(index: number) {
this.internalArray.splice(index, 1);
}
/**
* Gets the count of elements in list
*
* @readonly
* @type {number}
* @memberof SimpleList
* @wProperty Count
*/
get count(): number {
return this.internalArray.length;
}
/**
* Adds an element to the list
*
* @param {T} value
* @memberof SimpleList
* @wMethod Add
*/
add(value: T): void {
this.internalArray.push(value);
}
/**
* Clears the list
*
* @memberof SimpleList
* @wMethod Clear
*/
clear(): void {
this.internalArray.splice(0, this.internalArray.length);
}
/**
* Check if a value is contained in the list
*
* @param {T} value
* @returns {boolean}
* @memberof SimpleList
* @wMethod Contains
*/
contains(value: T): boolean {
return this.internalArray.indexOf(value) !== -1;
}
/**
* Removes a value from the list
*
* @param {T} value
* @returns {boolean}
* @memberof SimpleList
* @wMethod Remove
*/
remove(value: T): boolean {
const idx = this.internalArray.indexOf(value);
if (idx !== -1) {
this.internalArray.splice(idx, 1);
return true;
} else {
return false;
}
}
/**
* Performs an action on each element of the list
*
* @param {(e: T) => void} action
* @memberof SimpleList
* @wMethod ForEach
*/
forEach(action: (e: T) => void): void {
for (const obj of this.internalArray) {
action(obj);
}
}
/**
* Adds elements of an iterable to the list
*
* @param {Iterable<T>} range
* @memberof SimpleList
* @wMethod AddRange
*/
addRange(range: Iterable<T>): void {
for (const obj of range) {
this.add(obj);
}
}
/**
* Inserts elements of an interable to the list in a position
*
* @param {number} position
* @param {Iterable<T>} range
* @memberof SimpleList
* @wMethod InserRange
*/
insertRange(position: number, range: Iterable<T>): void {
if (!(position < this.internalArray.length && position >= 0)) {
throw new Error('Invalid position');
}
this.internalArray.splice(position, 0, ...range);
}
/**
* Sorts the list
*
* @memberof SimpleList
* @wMethod Sort
*/
sort();
sort(comparer: IComparer<T>);
sort(comparison: (x: T, y: T) => number);
sort(p?: any) {
if (typeof p === 'object') {
this.internalArray = this.internalArray.sort(p.Compare);
} else if (typeof p === 'function') {
this.internalArray = this.internalArray.sort(p);
} else {
if (
this.internalArray.length > 0 &&
typeof this.internalArray[0] === 'object'
) {
this.internalArray.sort((a: any, b: any) => {
if (a?.CompareTo && b?.CompareTo) {
return a.CompareTo(b);
} else {
return 0;
}
});
} else {
this.internalArray = this.internalArray.sort();
}
}
}
/**
* Copies the list to a target array
*
* @param {T[]} target
* @param {number} index
* @memberof SimpleList
* @wMethod CopyTo
*/
copyTo(target: T[], index: number): void {
for (let i = index; i < target.length; i++) {
target[i] = this.internalArray[i];
}
}
/**
* Removes all elements from the collection by a predicate
*
* @param {(e: T) => boolean} predicate
* @returns
* @memberof SimpleList
* @wMethod RemoveAll
*/
public removeAll(predicate: (e: T) => boolean) {
let removed = 0;
for (let i = this.internalArray.length - 1; i >= 0; i--) {
if (predicate(this.internalArray[i])) {
this.removeAt(i);
removed++;
}
}
return removed;
}
/**
* Converts the list to an array
*
* @returns
* @memberof SimpleList
* @wMethod ToArray
*/
public toArray() {
return [...this.internalArray];
}
[Symbol.iterator](): Iterator<T, any, undefined> {
return this.internalArray[Symbol.iterator]();
}
}
/**
* A collection of sort descriptions
*
* @export
* @class SortDescriptionCollection
* @extends {SimpleList<SortDescription>}
* @wType System.ComponentModel.SortDescriptionCollection
*/
export class SortDescriptionCollection
extends SimpleList<SortDescription>
implements INotifyCollectionChanged
{
CollectionChanged: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> = new SubscriptionEvent();
/**
* Raises the CollectionChanged event.
*
* @param {CollectionChangeInfo} info
* @memberof SortDescriptionCollection
*/
onCollectionChanged(info: CollectionChangeInfo): void {
this.CollectionChanged.fire([this, info]);
}
/**
* Inserts a new element at the specified position.
*
* @param {number} index
* @param {T} value
* @memberof SortDescriptionCollection
*/
insert(index: number, value: SortDescription): void {
super.insert(index, value);
const info = new CollectionChangeInfo(CollectionChangeAction.Add);
info.NewItems = new SimpleList([value]);
info.NewStartingIndex = index;
this.onCollectionChanged(info);
}
/**
* Removes the first element from the collection which matches the given value.
*
* @param {T} value
* @return {*} {boolean}
* @memberof SortDescriptionCollection
*/
remove(value: SortDescription): boolean {
const idx = this.internalArray.indexOf(value);
if (idx === -1) {
return false;
}
const result = super.remove(value);
const info = new CollectionChangeInfo(CollectionChangeAction.Remove);
info.OldItems = new SimpleList([value]);
info.OldStartingIndex = idx;
this.onCollectionChanged(info);
return result;
}
/**
* Removes the element at the given position.
*
* @param {number} index
* @memberof SortDescriptionCollection
*/
removeAt(index: number): void {
const oldItem = super.getItem(index);
super.removeAt(index);
const info = new CollectionChangeInfo(CollectionChangeAction.Remove);
info.OldItems = new SimpleList([oldItem]);
info.OldStartingIndex = index;
this.onCollectionChanged(info);
}
/**
* Adds the given element at the end of the collection.
*
* @param {T} value
* @memberof SortDescriptionCollection
*/
add(value: SortDescription): void {
super.add(value);
const info = new CollectionChangeInfo(CollectionChangeAction.Add);
info.NewItems = new SimpleList([value]);
info.NewStartingIndex = this.count - 1;
this.onCollectionChanged(info);
}
/**
* Removes all elements from the collection.
*
* @memberof SortDescriptionCollection
*/
clear(): void {
super.clear();
this.onCollectionChanged(
new CollectionChangeInfo(CollectionChangeAction.Reset)
);
}
/**
* Replace the element at the given position with the given element.
*
* @param {number} index
* @param {T} value
* @memberof SortDescriptionCollection
* @wMethod SetItem
*/
setItem(index: number, value: SortDescription): void {
const oldItem = super.getItem(index);
super.setItem(index, value);
const info = new CollectionChangeInfo(CollectionChangeAction.Replace);
info.OldItems = new SimpleList([oldItem]);
info.NewItems = new SimpleList([value]);
info.NewStartingIndex = index;
this.onCollectionChanged(info);
}
}
/**
* Reports changes in a collection.
*
* @export
* @class CollectionChangeInfo
* @wType System.Collections.Specialized.NotifyCollectionChangedEventArgs
*/
export class CollectionChangeInfo {
/**
* The items added or changed in the collection
*
* @type {IList<any>}
* @memberof CollectionChangeInfo
*/
public NewItems: IList<any> = null;
/**
* The index where the change occurred.
*
* @type {number}
* @memberof CollectionChangeInfo
*/
public NewStartingIndex = -1;
/**
* The items that where removed from the collection.
*
* @type {IList<any>}
* @memberof CollectionChangeInfo
*/
public OldItems: IList<any> = null;
/**
* The index where a replace action or remove occurred.
*
* @type {number}
* @memberof CollectionChangeInfo
*/
public OldStartingIndex = -1;
/**
* Creates an instance of CollectionChangeInfo.
*
* @param {CollectionChangeAction} action
* @memberof CollectionChangeInfo
*/
constructor(public action: CollectionChangeAction) {}
}
/**
* CollectionChangeAction enum
*
* @export
* @enum {number}
* @wEnum System.Collections.Specialized.NotifyCollectionChangedAction
*/
export enum CollectionChangeAction {
Add,
Remove,
Replace,
Reset,
}
/**
* INotifyCollectionChanged interface
*
* @export
* @interface INotifyCollectionChanged
* @wInterface System.Collections.Specialized.INotifyCollectionChanged
*/
export interface INotifyCollectionChanged {
CollectionChanged: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
>;
}
/**
* ObjectModelCollection class
*
* @export
* @class ObjectModelCollection
* @extends {SimpleList<T>}
* @template T
* @wType System.Collections.ObjectModel.Collection`1
*/
export class ObjectModelCollection<T> extends SimpleList<T> {}
/**
* ItemDataRequestedEventArgs class
*
* @export
* @class ItemDataRequestedEventArgs
* @wType Infragistics.Collections.ItemDataRequestedEventArgs
*/
export class ItemDataRequestedEventArgs {
/**
* FilterConditions property
*
* @type {*}
* @memberof ItemDataRequestedEventArgs
*/
FilterConditions: any = null;
/**
* ItemsCount property
*
* @type {number}
* @memberof ItemDataRequestedEventArgs
*/
ItemsCount: number = null;
/**
* SortDescriptions Property
*
* @type {*}
* @memberof ItemDataRequestedEventArgs
*/
SortDescriptions: any = null;
/**
* StartIndex property.
*
* @type {number}
* @memberof ItemDataRequestedEventArgs
*/
StartIndex: number = null;
}
/**
* PageChangingEventArgs class
*
* @export
* @class PageChangingEventArgs
* @wType System.ComponentModel.PageChangingEventArgs
*/
export class PageChangingEventArgs extends CancelEventArgs {
/**
* New page index internal field
*
* @private
* @type {number}
* @memberof PageChangingEventArgs
*/
private newPageIndex: number;
/**
*Creates an instance of PageChangingEventArgs.
* @param {number} newPageIndex
* @memberof PageChangingEventArgs
*/
constructor(newPageIndex: number) {
super();
this.newPageIndex = newPageIndex;
}
/**
* StartIndex property.
*
* @type {number}
* @memberof PageChangingEventArgs
*/
get NewPageIndex(): number {
return this.newPageIndex;
}
}
/**
* Collection which reports changes to its observers.
*
* @export
* @class ObservableCollection
* @extends {ObjectModelCollection<T>}
* @implements {INotifyPropertyChanged}
* @template T
* @wType System.Collections.ObjectModel.ObservableCollection`1
*/
export class ObservableCollection<T>
extends ObjectModelCollection<T>
implements INotifyCollectionChanged, INotifyPropertyChanged
{
/**
* Flag to indicate if supported interfaces have been initialized.
*
* @private
* @static
* @memberof ObservableCollection
*/
protected static interfacesInitialized = false;
/**
* Event to indicate that a property have changed.
*
* @memberof ObservableCollection
*/
public PropertyChanged: SubscriptionEvent<
(e: any, args: { PropertyName: string }) => void
> = new SubscriptionEvent();
/**
* Event to indicate that the collection have changed.
*
* @memberof ObservableCollection
*/
public CollectionChanged: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> = new SubscriptionEvent();
/**
* Event AddNewItem have changed.
*
* @memberof ObservableCollection
* @wIgnore
*/
public AddNewItem: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> = new SubscriptionEvent();
/**
* Creates an instance of ObservableCollection.
*
* @param {Iterable<T>} [initial]
* @memberof ObservableCollection
*/
constructor(initial?: Iterable<T>) {
super();
if (typeof initial !== 'undefined' && initial !== null) {
this.internalArray = [...initial];
}
ObservableCollection.initializeSupportedInterfaces();
}
/**
* Initialize the interfaces supported by this collection.
*
* @private
* @static
* @memberof ObservableCollection
*/
protected static initializeSupportedInterfaces(): void {
if (!ObservableCollection.interfacesInitialized) {
const setMetadataInfo = Reflect.metadata(
supportedInterfacesCompatibilityMetadataKey,
[
'System.ComponentModel.INotifyPropertyChanged',
'System.Collections.IEnumerable',
'System.Collections.Generic.IList`1',
'System.Collections.IList',
'System.Collections.Specialized.INotifyCollectionChanged',
]
);
setMetadataInfo(ObservableCollection);
ObservableCollection.interfacesInitialized = true;
}
}
/**
* Inserts a new element at the specified position.
*
* @param {number} index
* @param {T} value
* @memberof ObservableCollection
*/
insert(index: number, value: T): void {
super.insert(index, value);
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Add
);
changeEventInfo.NewItems = new SimpleList([value]);
changeEventInfo.NewStartingIndex = index;
this.onCollectionChanged(changeEventInfo);
this.onPropertyChanged({ PropertyName: 'Count' });
}
/**
* Removes the first element from the collection which matches the given value.
*
* @param {T} value
* @return {*} {boolean}
* @memberof ObservableCollection
*/
remove(value: T): boolean {
const idx = this.internalArray.indexOf(value);
if (idx === -1) {
return false;
}
const result = super.remove(value);
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Remove
);
changeEventInfo.OldItems = new SimpleList([value]);
changeEventInfo.OldStartingIndex = idx;
this.onCollectionChanged(changeEventInfo);
this.onPropertyChanged({ PropertyName: 'Count' });
return result;
}
/**
* Removes the element at the given position.
*
* @param {number} index
* @memberof ObservableCollection
*/
removeAt(index: number): void {
const oldItem = super.getItem(index);
super.removeAt(index);
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Remove
);
changeEventInfo.OldItems = new SimpleList([oldItem]);
changeEventInfo.OldStartingIndex = index;
this.onCollectionChanged(changeEventInfo);
this.onPropertyChanged({ PropertyName: 'Count' });
}
/**
* Adds the given element at the end of the collection.
*
* @param {T} value
* @memberof ObservableCollection
*/
add(value: T): void {
super.add(value);
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Add
);
changeEventInfo.NewItems = new SimpleList([value]);
changeEventInfo.NewStartingIndex = this.count - 1;
this.onCollectionChanged(changeEventInfo);
this.onPropertyChanged({ PropertyName: 'Count' });
}
/**
* Adds elements of an iterable to the list
*
* @param {Iterable<T>} range
* @memberof ObservableCollection
* @wIgnore
*/
silentAddRange(range: Iterable<T>) {
for (const obj of range) {
super.add(obj);
}
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Add
);
changeEventInfo.NewItems = new SimpleList([range]);
changeEventInfo.NewStartingIndex = this.count - 1;
this.onCollectionChanged(changeEventInfo);
this.onPropertyChanged({ PropertyName: 'Count' });
}
/**
* Removes all elements from the collection.
*
* @memberof ObservableCollection
*/
clear(): void {
super.clear();
this.onCollectionChanged(
new CollectionChangeInfo(CollectionChangeAction.Reset)
);
this.onPropertyChanged({ PropertyName: 'Count' });
}
/**
* Replace the element at the given position with the given element.
*
* @param {number} index
* @param {T} value
* @memberof ObservableCollection
* @wMethod SetItem
*/
setItem(index: number, value: T): void {
const oldItem = super.getItem(index);
super.setItem(index, value);
const changeEventInfo = new CollectionChangeInfo(
CollectionChangeAction.Replace
);
changeEventInfo.OldItems = new SimpleList([oldItem]);
changeEventInfo.NewItems = new SimpleList([value]);
changeEventInfo.NewStartingIndex = index;
this.onCollectionChanged(changeEventInfo);
}
/**
* Removes all elements from the collection.
*
* @protected
* @memberof ObservableCollection
* @wMethod ClearItems
*/
protected clearItems(): void {
this.clear();
}
/**
* Trigger a `CollectionChanged` event.
*
* @protected
* @param {CollectionChangeInfo} info
* @memberof ObservableCollection
* @wMethod OnCollectionChanged
*/
protected onCollectionChanged(info: CollectionChangeInfo): void {
this.CollectionChanged.fire([this, info]);
}
/**
* Trigger a `PropertyChanged` event.
*
* @protected
* @param {{ PropertyName: string }} info
* @memberof ObservableCollection
* @wMethod OnPropertyChanged
*/
protected onPropertyChanged(info: { PropertyName: string }): void {
this.PropertyChanged.fire([this, info]);
}
}
/**
* SimpleQueue class
*
* @export
* @class SimpleQueue
* @implements {Iterable<T>}
* @template T
* @wType System.Collections.Generic.Queue`1
*/
export class SimpleQueue<T> implements Iterable<T> {
public internalArray: T[] = [];
constructor(initialData?: Iterable<T>) {
if (typeof initialData !== 'undefined') {
this.internalArray = [...initialData];
}
}
/**
* Count the elements in queue
*
* @readonly
* @type {number}
* @memberof SimpleQueue
* @wProperty Count
*/
get count(): number {
return this.internalArray.length;
}
/**
* Enqueue an element in the queue
*
* @param {T} value
* @returns
* @memberof SimpleQueue
* @wMethod Enqueue
*/
public enqueue(value: T) {
return this.internalArray.push(value);
}
/**
* Dequeue an element of the queue
*
* @returns {T}
* @memberof SimpleQueue
* @wMethod Dequeue
*/
public dequeue(): T {
return this.internalArray.shift();
}
[Symbol.iterator](): Iterator<T, any, undefined> {
return this.internalArray[Symbol.iterator]();
}
}
/**
* ReadOnlyCollection class
*
* @export
* @class ReadonlyCollection
* @extends {SimpleList<T>}
* @template T
* @wType System.Collections.ObjectModel.ReadOnlyCollection`1
*/
export class ReadonlyCollection<T> extends SimpleList<T> {
constructor(contents?: Iterable<T>) {
super(contents);
}
// NOTE: this methods intentionally throw an exception, because
// it is not allowed to modify a read only collection.
insert(index: number, value: T) {
throw Error('Operation not supported');
}
remove(value: T): boolean {
throw Error('Operation not supported');
}
removeAt(index: number) {
throw Error('Operation not supported');
}
add(value: T): void {
throw Error('Operation not supported');
}
clear(): void {
throw Error('Operation not supported');
}
setItem(index: number, value: T) {
throw Error('Operation not supported');
}
}
//
// IterUtils
//
export function iuToArray<T>(iterable: Iterable<T>): Array<T> {
return [...iterable];
}
class SelectIteratorWrapper<T, K> implements Iterator<K> {
private index = 0;
constructor(
private innerIterator: Iterator<T>,
private funcWithIndex?: (e: T, i?: number) => K
) {}
next(): IteratorResult<K, any> {
const nextValue = this.innerIterator.next();
if (!nextValue.done) {
return {
value: this.funcWithIndex(nextValue.value, this.index++),
done: false,
};
}
return { value: undefined, done: true };
}
}
export function iuSelect<T, K>(
func: (e: T, i?: number) => K,
iterable: Iterable<T>
): Iterable<K> {
return {
[Symbol.iterator]() {
return new SelectIteratorWrapper(iterable[Symbol.iterator](), func);
},
};
}
class WhereIteratorWrapper<T> implements Iterator<T> {
constructor(
private innerIterator: Iterator<T>,
private predicate: (e: T) => boolean
) {}
next(value?: any): IteratorResult<T, any> {
let next = this.innerIterator.next();
while (!next.done && !this.predicate(next.value)) {
next = this.innerIterator.next();
}
return next;
}
}
export function iuWhere<T>(
predicate: (e: T) => boolean,
iterable: Iterable<T>
): Iterable<T> {
return {
[Symbol.iterator]() {
return new WhereIteratorWrapper(iterable[Symbol.iterator](), predicate);
},
};
}
export function iuFirst<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
): T {
if (
iterable instanceof Array &&
iterable.length > 0 &&
(typeof predicate === 'undefined' || predicate(iterable[0]))
) {
return iterable[0];
} else {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
while (!next.done) {
if (typeof predicate == 'undefined' || predicate(next.value)) {
return next.value;
}
next = iterator.next();
}
}
throw Error('Elements not available');
}
/**
* Returns the first element of the iterable for which predicate returns `true`,
* or a default value if the predicate returns `false` for every element.
* If no predicate is provided, returns the first element of the iterable, or
* a default value when the iterable is empty.
*
* @export
* @template T
* @param {Iterable<T>} iterable
* @param {(e: T) => boolean} [predicate]
* @param {T} [defaultValue=null]
* @return {*} {T}
*/
export function iuFirstOrDefault<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean,
defaultValue: T = null
): T {
if (
iterable instanceof Array &&
iterable.length > 0 &&
(typeof predicate === 'undefined' || predicate(iterable[0]))
) {
return iterable[0];
} else if (iterable != null) {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
while (!next.done) {
if (typeof predicate == 'undefined' || predicate(next.value)) {
return next.value;
}
next = iterator.next();
}
}
return defaultValue;
}
export function iuCount<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
): number {
if (iterable instanceof Array && !predicate) {
return iterable.length;
} else if (iterable instanceof SimpleList && !predicate) {
return iterable.count;
} else {
const iterator = iterable[Symbol.iterator]();
let i = 0;
let itResult = iterator.next();
while (!itResult.done) {
const current = itResult.value;
itResult = iterator.next();
if (predicate && !predicate(current)) {
continue;
}
i++;
}
return i;
}
}
class TakeIteratorWrapper<T> implements Iterator<T> {
constructor(private innerIterator: Iterator<T>, private count: number) {}
next(value?: any): IteratorResult<T, any> {
if (this.count > 0) {
this.count--;
const next = this.innerIterator.next();
return next;
} else {
return { value: undefined, done: true };
}
}
}
export function iuTake<T>(count: number, iterable: Iterable<T>): Iterable<T> {
return {
[Symbol.iterator]() {
return new TakeIteratorWrapper(iterable[Symbol.iterator](), count);
},
};
}
export function iuToList<T>(iterable: Iterable<T>): SimpleList<T> {
return new SimpleList(iterable);
}
export function iuLast<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
): T {
if (iterable instanceof Array && iterable.length > 0 && !predicate) {
return iterable[iterable.length - 1];
} else {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
let found = false;
let lastValue = null;
while (!next.done) {
if (predicate) {
if (predicate(next.value)) {
found = true;
lastValue = next.value;
}
} else {
found = true;
lastValue = next.value;
}
next = iterator.next();
}
if (!found) {
throw Error('Elements not available');
} else {
return lastValue;
}
}
}
export function iuLastOrDefault<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
): T {
if (iterable == null) {
throw Error('ArgumentNullException');
}
if (iterable instanceof Array && iterable.length > 0) {
if (predicate == undefined) {
return iterable[iterable.length - 1];
} else {
for (let i = iterable.length - 1; i >= 0; i--) {
if (predicate(iterable[i])) {
return iterable[i];
}
}
}
} else {
for (const val of Array.from(iterable).reverse()) {
if (predicate == undefined || predicate(val)) {
return val;
}
}
}
// here we have the problem of value types
// in this case we should not return null
// this work is still pending
return null;
}
export function iuElementAt<T>(iterable: Iterable<T>, index: number): T {
if (iterable instanceof Array && iterable.length > index && index >= 0) {
return iterable[index];
} else if (iterable instanceof SimpleList) {
return iterable.getItem(index);
} else {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
let found = false;
let lastValue;
let i = 0;
while (!next.done) {
if (i == index) {
found = true;
lastValue = next.value;
break;
}
next = iterator.next();
i++;
}
if (!found) {
throw Error('Elements not available');
} else {
return lastValue;
}
}
}
export function iuElementAtOrDefault<T>(
iterable: Iterable<T>,
index: number
): T {
if (iterable == null) {
throw Error('ArgumentNullException');
}
if (iterable instanceof Array && iterable.length > index && index >= 0) {
return iterable[index];
} else {
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
let i = 0;
while (!result.done) {
if (i == index) {
return result.value;
}
result = iterator.next();
i++;
}
}
return null;
}
export function iuAny<T>(iterable: Iterable<T>, predicate?: (e: T) => boolean) {
if (iterable instanceof Array && typeof predicate === 'undefined') {
return iterable.length > 0;
} else if (
iterable instanceof SimpleList &&
typeof predicate === 'undefined'
) {
return iterable.count > 0;
} else {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
if (typeof predicate === 'undefined') {
return !next.done;
} else {
let found = false;
while (!next.done) {
if (predicate(next.value)) {
found = true;
break;
}
next = iterator.next();
}
return found;
}
}
}
/**
* Verifies if a predicate is `true` for all the elements of an iterable
*
* @export
* @template T
* @param {Iterable<T>} iterable sequence to verify
* @param {(e: T) => boolean} [predicate] predicate
* @return {*} true if the predicate is valid for all elements of the sequence
*/
export function iuAll<T>(iterable: Iterable<T>, predicate: (e: T) => boolean) {
for (const element of iterable) {
if (!predicate(element)) {
return false;
}
}
return true;
}
export function iuSingle<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
) {
const errNotSingle = 'Sequence does not contain exactly one element';
const errNoElements =
'Sequence does not contain exactly one element that satisfies the condition';
let found = false;
let result: T = null;
if (
iterable instanceof Array &&
iterable.length == 1 &&
(typeof predicate == 'undefined' || predicate(iterable[0]))
) {
return iterable[0];
} else if (
iterable instanceof SimpleList &&
iterable.count == 1 &&
(typeof predicate == 'undefined' || predicate(iterable.getItem(0)))
) {
return iterable.getItem(0);
} else {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
while (!next.done) {
if (typeof predicate == 'undefined' || predicate(next.value)) {
if (found) {
throw new Error(errNotSingle);
}
result = next.value;
found = true;
}
next = iterator.next();
}
}
if (!found) {
throw new Error(errNoElements);
}
return result;
}
class ConcatIterator<T> implements Iterator<T> {
private firstFinished: boolean;
private iterator: Iterator<T>;
constructor(private iterable1: Iterable<T>, private iterable2: Iterable<T>) {
this.iterator = iterable1[Symbol.iterator]();
}
next(value?: any): IteratorResult<T> {
let result: IteratorResult<T> = null;
if (!this.firstFinished) {
result = this.iterator.next();
if (!result.done) {
return result;
}
this.firstFinished = true;
this.iterator = this.iterable2[Symbol.iterator]();
}
return this.iterator.next();
}
}
export function iuConcat<T>(
first: Iterable<T>,
second: Iterable<T>
): Iterable<T> {
if (first == null || second == null) {
throw new Error('ArgumentNullException');
}
return {
[Symbol.iterator]() {
return new ConcatIterator<T>(first, second);
},
};
}
/**
* Returns whether the iterable contains a specified element.
*
* @param iterable - The iterable source.
* @param value - The element to find.
*/
export function iuContains<T>(
iterable: Iterable<T>,
value: T,
comparer?: any
): boolean {
if (iterable == null) {
throw new Error('ArgumentNullException');
}
// Use 'Equals' to compare elements if available. If not, use '==='.
let isEqual: Function = getCompareFunction<T>(value);
for (const v of iterable) {
if (isEqual(value, v)) {
return true;
}
}
return false;
}
/**
* Base class for DistinctIterator, IntersectIterator and ExceptIterator.
*
* This class contains the logic to detect if the elements have an 'Equals()'
* function, and use it if available to compare elements. If 'Equals()' is
* not available, then '===' is used to compare elements.
*
* The logic to discriminate which elements are returned must be implemented
* on the derived classes.
*/
abstract class BaseDieIterator<T> implements Iterator<T> {
protected baseSet: Set<T>;
protected iterator: Iterator<T>;
protected nextResult: IteratorResult<T, any>;
protected baseSetHas: Function;
protected baseSetDelete: Function;
constructor(first: Iterable<T>, second: Iterable<T>) {
this.iterator = first[Symbol.iterator]();
this.baseSet = new Set(second);
this.baseSetHas = null;
// Get the first element to find out if it has an 'Equals()' function.
this.nextResult = this.iterator.next();
if (!this.nextResult.done) {
if (
this.nextResult?.value &&
typeof this.nextResult.value.Equals === 'function'
) {
// This is slow - O(n): use slow implementations of 'has()'
// and 'delete()' to use 'Equals()' to compare elements.
this.baseSetHas = (value: T): boolean =>
iuContains(this.baseSet, value);
this.baseSetDelete = (value: T): boolean => {
let found = false;
this.baseSet.forEach((x) => {
if (value['Equals'](x)) {
found = true;
this.baseSet.delete(x);
}
});
return found;
};
} else {
// This is fast - O(1): uses the Set directly, but Set does not use 'Equals()'.
// This approach is valid for value types and objects without 'Equals()'.
this.baseSetHas = (value: T): boolean => this.baseSet.has(value);
this.baseSetDelete = (value: T): boolean => this.baseSet.delete(value);
}
}
}
next(_value?: any): IteratorResult<T> {
if (this.nextResult.done) {
return this.nextResult;
}
while (!this.nextResult.done) {
if (this.returnCondition()) {
this.beforeReturn();
const return_result = this.nextResult;
this.nextResult = this.iterator.next();
return return_result;
}
this.nextResult = this.iterator.next();
}
return this.nextResult;
}
abstract returnCondition(): boolean;
abstract beforeReturn(): void;
}
class DistinctIterator<T> extends BaseDieIterator<T> {
constructor(private iterable: Iterable<T>) {
super(iterable, []);
}
returnCondition(): boolean {
return !this.baseSetHas(this.nextResult.value);
}
beforeReturn(): void {
this.baseSet.add(this.nextResult.value);
}
}
function getCompareFunction<T>(value: T): (x: any, y: any) => boolean {
let isEqual: (x: any, y: any) => boolean = null;
if (value && typeof value['Equals'] === 'function') {
isEqual = (left: T, right: T): boolean => left['Equals'](right);
} else {
isEqual = (left: T, right: T): boolean => left === right;
}
return isEqual;
}
/**
* Returns distinct elements from the iterable.
*
* @param iterable - The iterable source.
*/
export function iuDistinct<T>(
iterable: Iterable<T>,
comparer?: any
): Iterable<T> {
if (iterable == null) {
throw new Error('ArgumentNullException');
}
return {
[Symbol.iterator]() {
return new DistinctIterator<T>(iterable);
},
};
}
class IntersectIterator<T> extends BaseDieIterator<T> {
constructor(private first: Iterable<T>, private second: Iterable<T>) {
super(first, second);
}
returnCondition(): boolean {
return this.baseSetHas(this.nextResult.value);
}
beforeReturn(): void {
this.baseSetDelete(this.nextResult.value);
}
}
/**
* Returns the set intersection of two iterables.
*
* @param first
* @param second
*/
export function iuIntersect<T>(
first: Iterable<T>,
second: Iterable<T>,
comparer?: any
): Iterable<T> {
if (first == null || second == null) {
throw new Error('ArgumentNullException');
}
return {
[Symbol.iterator]() {
return new IntersectIterator<T>(first, second);
},
};
}
/**
* Defines a custom {Iterable<T>} object that will iterate thru two
* iterables by ignoring the ones in the second iterable that already exists in the
* first one.
*
* @class UnionIterator
* @implements {Iterable<T>}
* @template T
*/
class UnionIterator<T> implements Iterable<T> {
protected nextResult: IteratorResult<T, any>;
/**
* Creates an instance of UnionIterator for two iterable objects
* @param {Iterable<T>} first the iterable containing the first set of elements
* @param {Iterable<T>} second the iterable containing the second set of elements, the ones
* already in first will be discarded.
* @memberof UnionIterator
*/
constructor(private first: Iterable<T>, private second: Iterable<T>) {}
/**
* Defines the iterator for this class
*
* @return {*} {Iterator<T, any, any>}
* @memberof UnionIterator
*/
*[Symbol.iterator](): Iterator<T, any, any> {
for (const e of this.first) {
yield e;
}
for (const e of this.second) {
/* istanbul ignore else */
if (!iuContains(this.first, e)) {
yield e;
}
}
}
}
/**
* Iterates thru the elements of two iterables by disarding the duplicated
* ones. Default comparison is done using the standard '===' comparison operator
*
* @export
* @template T
* @param {Iterable<T>} first the iterable containing the first set of elements
* @param {Iterable<T>} second the iterable containing the second set of elements, the ones
* already in first will be discarded.
* @param {*} [comparer] The function used to compared elements. Currently not supported.
* @return {*} {Iterable<T>}
*/
export function iuUnion<T>(
first: Iterable<T>,
second: Iterable<T>,
comparer?: any
): Iterable<T> {
if (first == null || second == null) {
throw new Error('ArgumentNullException');
}
return new UnionIterator<T>(first, second);
}
class ExceptIterator<T> extends BaseDieIterator<T> {
constructor(private first: Iterable<T>, private second: Iterable<T>) {
super(first, second);
}
returnCondition(): boolean {
return !this.baseSetHas(this.nextResult.value);
}
beforeReturn(): void {
this.baseSet.add(this.nextResult.value);
}
}
/**
* Returns the set difference of two iterables.
*
* @param first - base iterable.
* @param second - the iterable to substract.
*/
export function iuExcept<T>(
first: Iterable<T>,
second: Iterable<T>
): Iterable<T> {
if (first == null || second == null) {
throw new Error('ArgumentNullException');
}
return {
[Symbol.iterator]() {
return new ExceptIterator<T>(first, second);
},
};
}
export function iuOrderBy<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any
): IOrderedIterable<T>;
export function iuOrderBy<T, K>(
iterable: Iterable<T>,
criteria?: (e: T) => K,
comparer?: IComparer<K>
): IOrderedIterable<T>;
export function iuOrderBy<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any,
comparer?: IComparer<any>
): IOrderedIterable<T> {
return new IterableForSorting(iterable, criteria, comparer);
}
export function iuOrderByDescending<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any
): IOrderedIterable<T>;
export function iuOrderByDescending<T, K>(
iterable: Iterable<T>,
criteria?: (e: T) => K,
comparer?: IComparer<K>
): IOrderedIterable<T>;
export function iuOrderByDescending<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any,
comparer?: IComparer<any>
): IOrderedIterable<T> {
return new IterableForSorting(
iterable,
criteria,
new DescendingComparer(comparer)
);
}
class DefaultOrderByComparer implements IComparer<unknown> {
Compare(e1: unknown, e2: unknown): number {
if (typeof e1 == 'number' && typeof e2 == 'number') {
return e1 - e2;
} else if (e1 instanceof Date && e2 instanceof Date) {
return e1.getTime() - e2.getTime();
} else if (typeof e1 == 'string' && typeof e2 == 'string') {
return e1.localeCompare(e2);
} else if (typeof e1 === 'boolean' && typeof e2 === 'boolean') {
return +e1 - +e2;
} else {
// fail with equal
return 0;
}
}
}
class DescendingComparer<T> implements IComparer<T> {
constructor(private comparer: IComparer<T>) {
if (comparer == null) {
this.comparer = new DefaultOrderByComparer();
}
}
Compare(x: T, y: T): number {
return this.comparer.Compare(y, x); // Just reverse elements.
}
}
class IterableForSorting<T> implements IOrderedIterable<T> {
private criterias: ((e: T) => unknown)[] = [];
private comparers: IComparer<unknown>[] = [];
constructor(
private iterable: Iterable<T>,
criteria: (e: T) => unknown,
comparer?: IComparer<unknown>
) {
this.criterias.push(criteria);
this.AddComparer(comparer);
}
CreateOrderedIterable<K>(
keySelector: (e: T) => K,
comparer: IComparer<K>,
descending: boolean
): IOrderedIterable<T> {
Debugger.Throw('Method not implemented.');
return null;
}
[Symbol.iterator](): Iterator<T, any, undefined> {
// until we find a better approach we will be converting
// the collection to an array
const collection = [...this.iterable];
collection.sort((o1, o2) => {
for (let i = 0; i < this.criterias.length; i++) {
const e1 = this.criterias[i](o1);
const e2 = this.criterias[i](o2);
const result = this.comparers[i].Compare(e1, e2);
if (result !== 0) {
return result;
}
}
return 0;
});
return collection[Symbol.iterator]();
}
ThenBy(criteria: (e: T) => unknown, comparer?: IComparer<unknown>): void {
this.criterias.push(criteria);
this.AddComparer(comparer);
}
private AddComparer(comparer?: IComparer<unknown>): void {
if (comparer == null) {
this.comparers.push(new DefaultOrderByComparer());
} else {
this.comparers.push(comparer);
}
}
}
export function iuThenBy<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any
): IOrderedIterable<T>;
export function iuThenBy<T, K>(
iterable: Iterable<T>,
criteria?: (e: T) => K,
comparer?: IComparer<K>
): IOrderedIterable<T>;
export function iuThenBy<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any,
comparer?: IComparer<any>
): IOrderedIterable<T> {
(iterable as IterableForSorting<T>).ThenBy(criteria, comparer);
return iterable as IOrderedIterable<T>;
}
export function iuThenByDescending<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any
): IOrderedIterable<T>;
export function iuThenByDescending<T, K>(
iterable: Iterable<T>,
criteria?: (e: T) => K,
comparer?: IComparer<K>
): IOrderedIterable<T>;
export function iuThenByDescending<T>(
iterable: Iterable<T>,
criteria?: (e: T) => any,
comparer?: IComparer<any>
): IOrderedIterable<T> {
(iterable as IterableForSorting<T>).ThenBy(
criteria,
new DescendingComparer(comparer)
);
return iterable as IOrderedIterable<T>;
}
export interface IGrouping<T, K> extends Iterable<T> {
Key: K;
}
class GroupByGrouping<T, K> extends SimpleList<T> implements IGrouping<T, K> {
public Key: K;
}
export function iuGroupBy<T, K>(
iterable: Iterable<T>,
keySelector: (e: T) => K
): Iterable<IGrouping<T, K>>;
export function iuGroupBy<T, K, V>(
iterable: Iterable<T>,
keySelector: (e: T) => K,
valueSelector: (e: T) => V
): Iterable<IGrouping<V, K>>;
export function iuGroupBy<T, K, V>(
iterable: Iterable<T>,
keySelector: (e: T) => K,
valueSelector?: (e: T) => V
): any {
const groups = new Map<string, GroupByGrouping<T | V, K>>();
for (const value of iterable) {
const rawKey = keySelector(value);
const key = (rawKey ?? '').toString();
let group: GroupByGrouping<T | V, K> = null;
if (groups.has(key)) {
group = groups.get(key);
} else {
group = new GroupByGrouping<T, K>();
groups.set(key, group);
group.Key = rawKey;
}
group.add(valueSelector ? valueSelector(value) : value);
}
return groups.values();
}
/**
* Gets a single element from the given iterable. If no predicate then the only element in iterable is returned,
* if more than one element then null is returned. If predicate has value then the only element matching the
* predicate is returned, if more than one matches or none then null is returned.
*
* @param iterable The {@link Iterable<T>} from where to extract the single element
* @param predicate a the predicate to apply to get the single element. If not predicate then the
* iterable value must contain exactly one element.
* @returns
*/
export function iuSingleOrDefault<T>(
iterable: Iterable<T>,
predicate?: (e: T) => boolean
) {
// no predicate, a single element iterable.
if (typeof predicate == 'undefined') {
const iterator = iterable[Symbol.iterator]();
const next = iterator.next();
if (!next.done) {
const result: T = next.value;
if (iterator.next().done) {
// checks for one element only
return result;
}
}
} else {
let matchingElement: T = null;
for (const e of iterable) {
const isMatch: boolean = predicate(e);
if (isMatch) {
if (matchingElement == null) {
matchingElement = e;
} else {
// more than one match returns null
throw new InvalidOperationException(
'more than one element in singleorDefault'
);
}
}
}
if (matchingElement != null) {
return matchingElement;
}
}
return null;
}
class FlattenIteratorWrapper<T, K, V> implements Iterator<V> {
private currentIterator: Iterator<V>;
constructor(
private mainIterator: Iterator<T>,
private func: (e: T) => Iterable<K>,
private resultSelector: (e: T, f: K) => V
) {}
next(value?: any): IteratorResult<V, any> {
if (this.currentIterator) {
const result = this.currentIterator.next();
if (!result.done) {
return result;
}
}
const nextInMain = this.mainIterator.next();
if (!nextInMain.done) {
this.currentIterator = iuSelect(
(innerSingleResult) =>
this.resultSelector(nextInMain.value, innerSingleResult),
this.func(nextInMain.value)
)[Symbol.iterator]();
// controversial recurisive call:
return this.next();
} else {
return { value: undefined, done: true };
}
}
}
export function iuSelectMany<T, K, V>(
func: (e: T) => Iterable<K>,
resultSelector: (e: T, f: K) => V,
iterable: Iterable<T>
): Iterable<V>;
export function iuSelectMany<T, K>(
func: (e: T) => Iterable<K>,
iterable: Iterable<T>
): Iterable<K>;
export function iuSelectMany<T, K, V>(
p1: unknown,
p2: unknown,
p3?: unknown
): unknown {
if (typeof p3 !== 'undefined') {
const iterable = p3 as Iterable<T>;
const func = p1 as (e: T) => Iterable<K>;
const resultSelector = p2 as (e: T, f: K) => V;
return {
[Symbol.iterator]() {
return new FlattenIteratorWrapper(
iterable[Symbol.iterator](),
func,
resultSelector
);
},
};
} else {
const iterable = p2 as Iterable<T>;
const func = p1 as (e: T) => Iterable<K>;
return {
[Symbol.iterator]() {
return new FlattenIteratorWrapper(
iterable[Symbol.iterator](),
func,
(e, f) => f
);
},
};
}
}
class RangeIterator implements Iterator<number> {
constructor(private current: number, private count: number) {}
next(value?: any): IteratorResult<number, any> {
if (this.count > 0) {
this.count--;
return { value: this.current++, done: false };
} else {
return { value: undefined, done: true };
}
}
}
export function iuRange(start: number, count: number): Iterable<number> {
return {
[Symbol.iterator]() {
return new RangeIterator(start, count);
},
};
}
const emptyIterable = {
[Symbol.iterator]() {
return { next: () => ({ done: true, value: undefined }) };
},
};
export function iuEmpty<T>(): Iterable<T> {
return emptyIterable;
}
export function iuAsEnumerable<T>(iterable: Iterable<T>): Iterable<T> {
return iterable;
}
export function iuCast<T>(iterable: Iterable<unknown>): Iterable<T> {
// The following code needs more work to avoid wrong conversions
// In typescript we will not get cast exceptions
return iuSelect((x) => x as T, iterable);
}
export function wrapArrayWithList<T>(arr: T[]): IList<T> {
const result = new SimpleList<T>();
result.internalArray = arr;
return result;
}
export function iuMax<T>(iterable: Iterable<T>): T;
export function iuMax<T, K>(
iterable: Iterable<T>,
valueSelector: (item: T) => K
): K;
export function iuMax<T, K>(
iterable: Iterable<T>,
valueSelector?: (item: T) => K
): K {
let max: K = null;
for (const item of iterable) {
const value = valueSelector ? valueSelector(item) : (item as unknown as K);
if (max === null) {
max = value;
}
if (value > max) {
max = value;
}
}
return max;
}
export function iuMin<T>(
iterable: Iterable<T>,
valueSelector?: (item: T) => number
) {
let min = Number.MAX_VALUE;
for (const item of iterable) {
const value = valueSelector
? valueSelector(item)
: (item as unknown as number);
if (value < min) {
min = value;
}
}
return min;
}
export function iuSum<T>(
iterable: Iterable<T>,
valueSelector: (item: T) => number
) {
let result = 0;
for (const item of iterable) {
const value = valueSelector(item);
result += value;
}
return result;
}
export function iuOfType<K>(
iterable: Iterable<unknown>,
type: any
): Iterable<K> {
// notices that runtime verification of interface types checking is still pending
return iuWhere(
(obj) => obj instanceof type,
iterable
) as unknown as Iterable<K>;
}
export function iuGetUntypedIterator<T>(iterable: Iterable<T>): any {
Debugger.Throw('Not implemented');
}
function* joinGenerator<TInner, TOuter, TKey, TResult>(
iterable: Iterable<TInner>,
iterable2: Iterable<TOuter>,
innerSelector: (inner: TInner) => TKey,
outerSelector: (outer: TOuter) => TKey,
resultSelector: (inner: TInner, outer: TOuter) => TResult
) {
for (const x of iterable) {
const x_key = innerSelector(x);
for (const y of iterable2) {
const y_key = outerSelector(y);
const compare = getCompareFunction(x_key);
if (compare(x_key, y_key)) {
yield resultSelector(x, y);
}
}
}
}
export function iuJoin<TInner, TOuter, TKey, TResult>(
iterable: Iterable<TInner>,
iterable2: Iterable<TOuter>,
innerSelector: (inner: TInner) => TKey,
outerSelector: (outer: TOuter) => TKey,
resultSelector: (inner: TInner, outer: TOuter) => TResult
): Iterable<TResult> {
return {
[Symbol.iterator]() {
return joinGenerator(
iterable,
iterable2,
innerSelector,
outerSelector,
resultSelector
);
},
};
}
/**
* Apply an operation to each element of an Iterable
*
* @export
* @template T
* @param {Iterable<T>} iterable iterable to process
* @param {(e: T, i? : number) => void} func opeartion to apply
*/
export function iuForEach<T>(
iterable: Iterable<T>,
func: (e: T, i?: number) => void
): void {
for (const element of iterable) {
func(element);
}
}
/**
* Adds the contents of an iterable to the given collection
*
* @export
* @template T
* @param {ObjectModelCollection<T>} collection to modified
* @param {Iterable<T>} newElements to add
*/
export function iuAddRange<T>(
collection: ObjectModelCollection<T>,
newElements: Iterable<T>
) {
for (const obj of newElements) {
collection.add(obj);
}
}