@mezzy/collections
Version:
A luxurious user experience framework, developed by your friends at Mezzanine.
213 lines (167 loc) • 7 kB
text/typescript
import is from '@mezzy/is';
import {
ICompareFunction,
IEqualsFunction,
ILoopFunction
} from '@mezzy/function-types';
import ArrayTools from './arrayTools';
import IList from './interfaces/iList';
export class List<T> implements IList<T> {
constructor(items?: T[]) {
this.p_array = [];
if (is.notEmpty(items)) {
for (let i:number = 0; i < items.length; i++) { this.add(items[i]); }
}
}
/*====================================================================*
START: Static
*====================================================================*/
static fromArray<T>(array:T[]):List<T> {
let list:List<T> = new List<T>();
for (let i:number = 0; i < array.length; i++) { list.add(array[i]); }
return list;
}
/*====================================================================*
START: Properties
*====================================================================*/
get isEmpty():boolean { return this.p_array.length <= 0; }
get size():number { return this.p_array.length; }
get first():T { return this.p_array[0]; }
get last():T { return this.p_array[this.p_array.length]; }
get array():Array<T> { return this.p_array; }
protected p_array:Array<T>;
item(index:number):T {
let item:T = this.p_array[index];
return is.notEmpty(item) ? item : null;
}
/*====================================================================*
START: Methods
*====================================================================*/
indexOf(item:T, fromIndex:number = 0):number { return this.p_array.indexOf(item, fromIndex); }
add(item:T, index?:number):void {
if (is.empty(index)) index = this.p_array.length;
if (index < 0 || index > this.p_array.length || is.empty(item)) return;
this.p_array.splice(index, 0, item);
}
/**
* Appends all the items in the provided list to this list.
*/
append(list:List<T>):void {
if (is.empty(list)) return;
list.forEach((item:T) => {
if (is.notEmpty) {
this.add(item);
return true;
} else return false;
});
}
copy():List<T> {
let list:List<T> = new List<any>();
for (let index = 0; index < this.p_array.length; index++) {
list.add(this.p_array[index])
}
return list;
}
forEach(callback:ILoopFunction<T>):void {
this.p_array.forEach(callback);
}
sort(compareFunction:ICompareFunction<T>):void {
this.p_array.sort(compareFunction);
}
/**
* Replaces/updates an element in this list.
* Returns true if the element was replaced or false if the index is invalid or if the element is undefined.
*/
replace(item:T, index:number):void { this.p_array.splice(index, 1, item); }
/**
* Returns the index in this list of the first occurrence of the
* specified element, or -1 if the List does not contain this element.
* <p>If the elements inside this list are
* not comparable with the === operator a custom equals function should be
* provided to perform searches, the function must receive two arguments and
* return true if they are equal, false otherwise. Example:</p>
*
* <pre>
* let petsAreEqualByName = function(pet1, pet2) {
* return pet1.key === pet2.key;
* }
* </pre>
*/
search(item:T, equalsFunction?:IEqualsFunction<T>):number {
if (is.empty(equalsFunction)) equalsFunction = (a:T, b:T):boolean => { return a === b; };
if (is.empty(item)) return -1;
for (let index = 0; index < this.p_array.length; index++) {
if (equalsFunction(this.p_array[index], item)) return index;
}
return -1;
}
/**
* Returns true if this list has the specified element.
* <p>If the elements inside the list are
* not comparable with the === operator a custom equals function should be
* provided to perform searches, the function must receive two arguments and
* return true if they are equal, false otherwise. Example:</p>
*
* <pre>
* let petsAreEqualByName = function(pet1, pet2) {
* return pet1.key === pet2.key;
* }
* </pre>
*/
has(item:T):boolean { return (this.p_array.indexOf(item) >= 0); }
// has(item:T, equalsFunction?:IEqualsFunction<T>):boolean {
// return (this.indexOf(item, equalsFunction) >= 0);
// }
/**
* Removes the first occurrence of the specified element in this list.
* <p>If the elements inside the list are
* not comparable with the === operator a custom equals function should be
* provided to perform searches, the function must receive two arguments and
* return true if they are equal, false otherwise. Example:</p>
*
* <pre>
* let petsAreEqualByName = (pet1, pet2) => {
* return pet1.key === pet2.key;
* }
* </pre>
*/
delete(item:T, equalsFunction?:IEqualsFunction<T>):boolean {
if (is.empty(equalsFunction)) equalsFunction = (a:T, b:T):boolean => { return a === b; };
if (this.p_array.length < 1 || is.empty(item)) return false;
for (let index = 0; index < this.p_array.length; index++) {
if (equalsFunction(this.p_array[index], item)) {
this.p_array.splice(index, 1);
return true;
}
}
return false;
}
deleteAtIndex(index:number):T {
if (index < 0 || index >= this.p_array.length) return undefined;
let item:T = this.p_array[index];
this.p_array.splice(index, 1);
return item;
}
clear():void { this.p_array.splice(0, this.p_array.length); }
/**
* Returns true if this list is equal to the given list.
* Two lists are equal if they have the same elements in the same order.
* @param {List} other the other list.
* @param {function(Object,Object):boolean=} equalsFunction optional
* function used to check if two elements are equal. If the elements in the lists
* are custom objects you should provide a function, otherwise
* the === operator is used to check equality between elements.
* @return {boolean} true if this list is equal to the given list.
*/
equals(other:List<T>, equalsFunction?:IEqualsFunction<T>):boolean {
if (is.empty(equalsFunction)) equalsFunction = (a:T, b:T):boolean => { return a === b; };
if (!(other instanceof List)) return false;
if (this.size !== other.size) return false;
for (let index = 0; index < this.p_array.length; index++) {
if (!equalsFunction(this.p_array[index], other.array[index])) return false;
}
return true;
}
toString():string { return ArrayTools.toString(this.copy().array); }
} // End class
export default List;