feat(core): iterable ObservableArray (#9824)

BREAKING CHANGE:

Method push will now handle arguments just like Array.prototype.push.
Certain existing methods will now return ObservableArray instance instead.
Callback arguments that contained an array argument themselves will now contain an ObservableArray argument.
This commit is contained in:
Dimitris-Rafail Katsampas
2022-07-09 19:20:01 +03:00
committed by Nathan Walker
parent ad01e6b990
commit df74a8baa8
3 changed files with 153 additions and 130 deletions

View File

@ -14,13 +14,13 @@ export const test_ObservableArray_shouldCopySourceArrayItems = function () {
}; };
export const test_ObservableArray_shouldCopyMultipleItemsAsSource = function () { export const test_ObservableArray_shouldCopyMultipleItemsAsSource = function () {
// // >> observable-array-arguments // >> observable-array-arguments
// const array = new ObservableArray(1, 2, 3); const sa = [1, 2, 3];
// // << observable-array-arguments const array = new ObservableArray(...sa);
// << observable-array-arguments
// TKUnit.assertEqual(array.length, 3, "ObservableArray length should be 3"); TKUnit.assertEqual(array.length, 3, 'ObservableArray length should be 3');
// TKUnit.assertEqual(array.getItem(1), 2, "ObservableArray should copy multiple items from source!"); TKUnit.assertEqual(sa.length, array.length, 'ObservableArray should copy multiple items as source!');
TKUnit.assertEqual(true, true);
}; };
export const test_ObservableArray_shouldCreateArrayFromSpecifiedLength = function () { export const test_ObservableArray_shouldCreateArrayFromSpecifiedLength = function () {
@ -43,6 +43,15 @@ export const test_ObservableArray_shouldBeAbleToSetLength = function () {
TKUnit.assertEqual(array.length, 50, 'ObservableArray should respect new length!'); TKUnit.assertEqual(array.length, 50, 'ObservableArray should respect new length!');
}; };
export const test_ObservableArray_shouldBeIterable = function () {
// >> observable-array-iterable
const array = new ObservableArray([1, 2, 3]);
const iterator = array[Symbol.iterator]();
// << observable-array-iterable
TKUnit.assertEqual(iterator.next?.()?.value, array.getItem(0), 'ObservableArray should be iterable!');
};
export const test_ObservableArray_getItemShouldReturnCorrectItem = function () { export const test_ObservableArray_getItemShouldReturnCorrectItem = function () {
// >> observable-array-getitem // >> observable-array-getitem
const array = new ObservableArray([1, 2, 3]); const array = new ObservableArray([1, 2, 3]);
@ -54,6 +63,17 @@ export const test_ObservableArray_getItemShouldReturnCorrectItem = function () {
TKUnit.assert(firstItem === 1 && secondItem === 2 && thirdItem === 3, 'ObservableArray getItem() should return correct item!'); TKUnit.assert(firstItem === 1 && secondItem === 2 && thirdItem === 3, 'ObservableArray getItem() should return correct item!');
}; };
export const test_ObservableArray_getItemShouldReturnCorrectItemForNegativeIndex = function () {
// >> observable-array-getitem
const array = new ObservableArray([1, 2, 3]);
const thirdItem = array.getItem(-1);
const secondItem = array.getItem(-2);
const firstItem = array.getItem(-3);
// << observable-array-getitem
TKUnit.assert(thirdItem === 3 && secondItem === 2 && firstItem === 1, 'ObservableArray getItem() should return correct item for negative index!');
};
export const test_ObservableArray_setItemShouldSetCorrectItem = function () { export const test_ObservableArray_setItemShouldSetCorrectItem = function () {
// >> observable-array-setitem // >> observable-array-setitem
const array = new ObservableArray([1, 2, 3]); const array = new ObservableArray([1, 2, 3]);
@ -62,6 +82,14 @@ export const test_ObservableArray_setItemShouldSetCorrectItem = function () {
TKUnit.assert(array.getItem(1) === 5, 'ObservableArray setItem() should set correct item!'); TKUnit.assert(array.getItem(1) === 5, 'ObservableArray setItem() should set correct item!');
}; };
export const test_ObservableArray_setItemShouldSetCorrectItemForNegativeIndex = function () {
// >> observable-array-setitem
const array = new ObservableArray([1, 2, 3]);
array.setItem(-2, 5);
// << observable-array-setitem
TKUnit.assert(array.getItem(-2) === 5, 'ObservableArray setItem() should set correct item for negative index!');
};
export const test_ObservableArray_setItemShouldRaiseCorrectEvent = function () { export const test_ObservableArray_setItemShouldRaiseCorrectEvent = function () {
// >> observable-array-eventdata // >> observable-array-eventdata
let index: number; let index: number;
@ -84,12 +112,12 @@ export const test_ObservableArray_setItemShouldRaiseCorrectEvent = function () {
TKUnit.assertEqual(removed[0], 2); TKUnit.assertEqual(removed[0], 2);
}; };
export const test_ObservableArray_concatShouldReturnNewArrayWithNewItemsAtTheEnd = function () { export const test_ObservableArray_concatShouldReturnNewObservableArrayWithNewItemsAtTheEnd = function () {
// >> observable-array-combine // >> observable-array-combine
const array = new ObservableArray([1, 2, 3]); const array = new ObservableArray([1, 2, 3]);
const result = array.concat([4, 5, 6]); const result = array.concat([4, 5, 6]);
// << observable-array-combine // << observable-array-combine
TKUnit.assert(result.length === 6 && result[4] === 5, 'ObservableArray concat() should add items at the end!'); TKUnit.assert(result.length === 6 && result.getItem(4) === 5, 'ObservableArray concat() should add items at the end!');
}; };
export const test_ObservableArray_joinShouldReturnStringWithAllItemsSeparatedWithComma = function () { export const test_ObservableArray_joinShouldReturnStringWithAllItemsSeparatedWithComma = function () {
@ -226,50 +254,12 @@ export const test_ObservableArray_pushShouldAppendNewElementsAndRaiseChangeEvent
TKUnit.assert(result.eventName === ObservableArray.changeEvent && result.action === ChangeType.Add && result.removed.length === 0 && result.index === 3 && result.addedCount === 3, "ObservableArray push() should raise 'change' event with correct args!"); TKUnit.assert(result.eventName === ObservableArray.changeEvent && result.action === ChangeType.Add && result.removed.length === 0 && result.index === 3 && result.addedCount === 3, "ObservableArray push() should raise 'change' event with correct args!");
}; };
export const test_ObservableArray_pushShouldAppendNewElementsFromSourceArray = function () { export const test_ObservableArray_reverseShouldReturnReversedObservableArray = function () {
// >> observable-array-push-source
const array = new ObservableArray([1, 2, 3]);
// >> (hide)
const viewBase = new Label();
viewBase.set('testProperty', 0);
viewBase.bind({ sourceProperty: 'length', targetProperty: 'testProperty' }, array);
// << (hide)
const result = array.push([4, 5, 6]);
// << observable-array-push-source
TKUnit.assert(result === 6 && array.getItem(5) === 6, 'ObservableArray push() should append new elements from source array!');
TKUnit.assert(viewBase.get('testProperty') === array.length, 'Expected: ' + array.length + ', Actual: ' + viewBase.get('testProperty'));
};
export const test_ObservableArray_pushShouldAppendNewElementsFromSourceArrayAndRaiseChangeEventWithCorrectArgs = function () {
let result: ChangedData<number>;
// >> observable-array-push-source-info
const array = new ObservableArray([1, 2, 3]);
array.on(ObservableArray.changeEvent, (args: ChangedData<number>) => {
// Argument (args) is ChangedData<T>.
// args.eventName is "change".
// args.action is "add".
// args.index is equal to the array length.
// args.removed.length is 0.
// args.addedCount is equal to the number of added items.
// >> (hide)
result = args;
// << (hide)
});
array.push([4, 5, 6]);
// << observable-array-push-source-info
TKUnit.assert(result.eventName === ObservableArray.changeEvent && result.action === ChangeType.Add && result.removed.length === 0 && result.index === 3 && result.addedCount === 3, "ObservableArray push() should raise 'change' event with correct args!");
};
export const test_ObservableArray_reverseShouldReturnNewReversedArray = function () {
// >> observable-array-reverse // >> observable-array-reverse
const array = new ObservableArray([1, 2, 3]); const array = new ObservableArray([1, 2, 3]);
const result = array.reverse(); const result = array.reverse();
// << observable-array-reverse // << observable-array-reverse
TKUnit.assert(result.length === 3 && result[0] === 3, 'ObservableArray reverse() should return new reversed array!'); TKUnit.assert(result.length === 3 && result.getItem(0) === 3, 'ObservableArray reverse() should return reversed observable array!');
}; };
export const test_ObservableArray_shiftShouldRemoveTheFirstElement = function () { export const test_ObservableArray_shiftShouldRemoveTheFirstElement = function () {
@ -311,36 +301,36 @@ export const test_ObservableArray_shiftShouldRemoveTheFirstElementAndRaiseChange
TKUnit.assert(result.eventName === ObservableArray.changeEvent && result.action === ChangeType.Delete && result.removed.length === 1 && result.index === 0 && result.addedCount === 0, "ObservableArray shift() should raise 'change' event with correct args!"); TKUnit.assert(result.eventName === ObservableArray.changeEvent && result.action === ChangeType.Delete && result.removed.length === 1 && result.index === 0 && result.addedCount === 0, "ObservableArray shift() should raise 'change' event with correct args!");
}; };
export const test_ObservableArray_sliceShouldReturnSectionAsNewArray = function () { export const test_ObservableArray_sliceShouldReturnSectionAsNewObservableArray = function () {
// >> observable-array-slice // >> observable-array-slice
const array = new ObservableArray([1, 2, 3]); const array = new ObservableArray([1, 2, 3]);
const result = array.slice(); const result = array.slice();
// << observable-array-slice // << observable-array-slice
TKUnit.assert(result[2] === 3 && result.length === 3, 'ObservableArray slice() should return section!'); TKUnit.assert(result.getItem(2) === 3 && result.length === 3, 'ObservableArray slice() should return section!');
}; };
export const test_ObservableArray_sliceWithParamsShouldReturnSectionAsNewArray = function () { export const test_ObservableArray_sliceWithParamsShouldReturnSectionAsNewObservableArray = function () {
// >> observable-array-slice-args // >> observable-array-slice-args
const array = new ObservableArray([1, 2, 3, 4, 5]); const array = new ObservableArray([1, 2, 3, 4, 5]);
const result = array.slice(2, 4); const result = array.slice(2, 4);
// << observable-array-slice-args // << observable-array-slice-args
TKUnit.assert(result[1] === 4 && result.length === 2, 'ObservableArray slice() should return section according to specified arguments!'); TKUnit.assert(result.getItem(1) === 4 && result.length === 2, 'ObservableArray slice() should return section according to specified arguments!');
}; };
export const test_ObservableArray_sortShouldReturnNewSortedArray = function () { export const test_ObservableArray_sortShouldReturnSortedObservableArray = function () {
// >> observable-array-sort // >> observable-array-sort
const array = new ObservableArray([3, 2, 1]); const array = new ObservableArray([3, 2, 1]);
const result = array.sort(); const result = array.sort();
// << observable-array-sort // << observable-array-sort
TKUnit.assert(result[0] === 1 && result.length === 3, 'ObservableArray sort() should return new sorted array!'); TKUnit.assert(result.getItem(0) === 1 && result.length === 3, 'ObservableArray sort() should return sorted observable array!');
}; };
export const test_ObservableArray_sortShouldReturnNewSortedArrayAccordingSpecifiedOrder = function () { export const test_ObservableArray_sortShouldReturnSortedObservableArrayAccordingSpecifiedOrder = function () {
// >> observable-array-sort-comparer // >> observable-array-sort-comparer
const array = new ObservableArray([10, 100, 1]); const array = new ObservableArray([10, 100, 1]);
const result = array.sort((a: number, b: number) => a - b); const result = array.sort((a: number, b: number) => a - b);
// << observable-array-sort-comparer // << observable-array-sort-comparer
TKUnit.assert(result[2] === 100 && result.length === 3, 'ObservableArray sort() should return new sorted array according to specified order!'); TKUnit.assert(result.getItem(2) === 100 && result.length === 3, 'ObservableArray sort() should return sorted observable array according to specified order!');
}; };
export const test_ObservableArray_spliceShouldRemoveSpecifiedNumberOfElementsStartingFromSpecifiedIndex = function () { export const test_ObservableArray_spliceShouldRemoveSpecifiedNumberOfElementsStartingFromSpecifiedIndex = function () {
@ -353,7 +343,7 @@ export const test_ObservableArray_spliceShouldRemoveSpecifiedNumberOfElementsSta
// << (hide) // << (hide)
const result = array.splice(1, 2); const result = array.splice(1, 2);
// << observable-array-splice // << observable-array-splice
TKUnit.assert(result.length === 2 && result[0] === 'two' && array.length === 1 && array.getItem(0) === 'one', 'ObservableArray splice() should remove specified number of elements starting from specified index!'); TKUnit.assert(result.length === 2 && result.getItem(0) === 'two' && array.length === 1 && array.getItem(0) === 'one', 'ObservableArray splice() should remove specified number of elements starting from specified index!');
TKUnit.assert(viewBase.get('testProperty') === array.length, 'Expected: ' + array.length + ', Actual: ' + viewBase.get('testProperty')); TKUnit.assert(viewBase.get('testProperty') === array.length, 'Expected: ' + array.length + ', Actual: ' + viewBase.get('testProperty'));
}; };
@ -466,7 +456,7 @@ export const test_ObservableArray_spliceShouldInsertNewItemsInPlaceOfRemovedItem
const array = new ObservableArray(['one', 'two', 'three']); const array = new ObservableArray(['one', 'two', 'three']);
const result = array.splice(1, 2, 'six', 'seven'); const result = array.splice(1, 2, 'six', 'seven');
// << observable-array-splice-args // << observable-array-splice-args
TKUnit.assert(result.length === 2 && result[0] === 'two' && array.length === 3 && array.getItem(2) === 'seven', 'ObservableArray splice() should insert new items in place of removed!'); TKUnit.assert(result.length === 2 && result.getItem(0) === 'two' && array.length === 3 && array.getItem(2) === 'seven', 'ObservableArray splice() should insert new items in place of removed!');
}; };
export const test_ObservableArray_spliceShouldRemoveAndInertSpecifiedNumberOfElementsStartingFromSpecifiedIndexAndRaiseChangeEventWithCorrectArgs = function () { export const test_ObservableArray_spliceShouldRemoveAndInertSpecifiedNumberOfElementsStartingFromSpecifiedIndexAndRaiseChangeEventWithCorrectArgs = function () {
@ -629,6 +619,10 @@ export const test_ObservableArray_settingLengthToSomethingPerformsSpliceAdded =
const array = new ObservableArray(); const array = new ObservableArray();
export const test_symbolIterator_isDefined = function () {
TKUnit.assert(typeof array[Symbol.iterator] === 'function', "Method '[Symbol.iterator]()' should be defined!");
};
// We do not have indexer! // We do not have indexer!
export const test_getItem_isDefined = function () { export const test_getItem_isDefined = function () {
TKUnit.assert(typeof array.getItem === 'function', "Method 'getItem()' should be defined!"); TKUnit.assert(typeof array.getItem === 'function', "Method 'getItem()' should be defined!");
@ -643,12 +637,28 @@ export const test_length_isDefined = function () {
TKUnit.assert(typeof array.length === 'number', "Property 'length' should be defined!"); TKUnit.assert(typeof array.length === 'number', "Property 'length' should be defined!");
}; };
export const test_includes_isDefined = function () {
TKUnit.assert(typeof array.includes === 'function', "Method 'includes()' should be defined!");
};
export const test_find_isDefined = function () {
TKUnit.assert(typeof array.find === 'function', "Method 'find()' should be defined!");
};
export const test_findIndex_isDefined = function () {
TKUnit.assert(typeof array.findIndex === 'function', "Method 'findIndex()' should be defined!");
};
export const test_toJSON_isDefined = function () {
TKUnit.assert(typeof array.toJSON === 'function', "Method 'toJSON()' should be defined!");
};
export const test_toString_isDefined = function () { export const test_toString_isDefined = function () {
TKUnit.assert(typeof array.toString === 'function', "Method 'toString()' should be defined!"); TKUnit.assert(typeof array.toString === 'function', "Method 'toString()' should be defined!");
}; };
export const test_toLocaleString_isDefined = function () { export const test_toLocaleString_isDefined = function () {
TKUnit.assert(typeof array.toLocaleString === 'function', "Method 'toString()' should be defined!"); TKUnit.assert(typeof array.toLocaleString === 'function', "Method 'toLocaleString()' should be defined!");
}; };
export const test_concat_isDefined = function () { export const test_concat_isDefined = function () {

View File

@ -20,6 +20,9 @@ previous_url: /ApiReference/data/observable-array/HOW-TO
### Set ObservableArray length to new value. ### Set ObservableArray length to new value.
{%snippet observable-array-newvalue%} {%snippet observable-array-newvalue%}
### Set ObservableArray to be iterable.
{%snippet observable-array-iterable%}
### Get item at specified index using getItem(index) method. ### Get item at specified index using getItem(index) method.
{%snippet observable-array-getitem%} {%snippet observable-array-getitem%}
@ -56,12 +59,6 @@ previous_url: /ApiReference/data/observable-array/HOW-TO
### Handle "change" event to know more info about the change after calling push() method with multiple elements. ### Handle "change" event to know more info about the change after calling push() method with multiple elements.
{%snippet observable-array-push-multiple-info%} {%snippet observable-array-push-multiple-info%}
### Use push() method to add multiple elements from source array to the ObservableArray.
{%snippet observable-array-push-source%}
### Handle "change" event to know more info about the change after calling push() method with multiple elements from source array.
{%snippet observable-array-push-source-info%}
### Use reverse() method to reverse the elements order of the ObservableArray. ### Use reverse() method to reverse the elements order of the ObservableArray.
{%snippet observable-array-reverse%} {%snippet observable-array-reverse%}

View File

@ -82,17 +82,27 @@ export class ObservableArray<T> extends Observable {
}; };
} }
*[Symbol.iterator]() {
for (let item of this._array) {
yield item;
}
}
/** /**
* Returns item at specified index. * Returns item at specified position.
* Supports relative indexing from the end of the array when passed a negative index.
*/ */
getItem(index: number): T { getItem(pos: number): T {
const index = pos < 0 ? this._array.length + pos : pos;
return this._array[index]; return this._array[index];
} }
/** /**
* Sets item at specified index. * Sets item at specified position.
* Supports relative indexing from the end of the array when passed a negative index.
*/ */
setItem(index: number, value: T) { setItem(pos: number, value: T) {
const index = pos < 0 ? this._array.length + pos : pos;
const oldValue = this._array[index]; const oldValue = this._array[index];
this._array[index] = value; this._array[index] = value;
@ -115,14 +125,15 @@ export class ObservableArray<T> extends Observable {
set length(value: number) { set length(value: number) {
if (types.isNumber(value) && this._array && this._array.length !== value) { if (types.isNumber(value) && this._array && this._array.length !== value) {
const added = []; const added = new Array(value > this._array.length ? value - this._array.length : 0);
for (let i = this._array.length; i < value; ++i) {
added.push(undefined);
}
this.splice(value, this._array.length - value, ...added); this.splice(value, this._array.length - value, ...added);
} }
} }
toJSON(): Array<any> {
return this._array;
}
/** /**
* Returns a string representation of an array. * Returns a string representation of an array.
*/ */
@ -138,11 +149,11 @@ export class ObservableArray<T> extends Observable {
* Combines two or more arrays. * Combines two or more arrays.
* @param items Additional items to add to the end of array1. * @param items Additional items to add to the end of array1.
*/ */
concat(...args): T[] { concat(...args): ObservableArray<T> {
this._addArgs.index = this._array.length; this._addArgs.index = this._array.length;
const result = this._array.concat(...args); const result = this._array.concat(...args);
return result; return new ObservableArray<T>(result);
} }
/** /**
@ -174,19 +185,11 @@ export class ObservableArray<T> extends Observable {
* @param item New element of the Array. * @param item New element of the Array.
*/ */
push(...args: any): number { push(...args: any): number {
this._addArgs.index = this._array.length; const length = this._array.length;
const result = this._array.push(...args);
if (arguments.length === 1 && Array.isArray(arguments[0])) { this._addArgs.index = length;
const source = <Array<T>>arguments[0]; this._addArgs.addedCount = result - length;
for (let i = 0, l = source.length; i < l; i++) {
this._array.push(source[i]);
}
} else {
this._array.push(...args);
}
this._addArgs.addedCount = this._array.length - this._addArgs.index;
this.notify(this._addArgs); this.notify(this._addArgs);
this._notifyLengthChange(); this._notifyLengthChange();
@ -201,9 +204,11 @@ export class ObservableArray<T> extends Observable {
/** /**
* Reverses the elements in an Array. * Reverses the elements in an Array.
* This method uses 'in place' algorithm.
*/ */
reverse(): T[] { reverse(): ObservableArray<T> {
return this._array.reverse(); this._array.reverse();
return this;
} }
/** /**
@ -226,25 +231,29 @@ export class ObservableArray<T> extends Observable {
* @param start The beginning of the specified portion of the array. * @param start The beginning of the specified portion of the array.
* @param end The end of the specified portion of the array. * @param end The end of the specified portion of the array.
*/ */
slice(start?: number, end?: number): T[] { slice(start?: number, end?: number): ObservableArray<T> {
return this._array.slice(start, end); const result = this._array.slice(start, end);
return new ObservableArray<T>(result);
} }
/** /**
* Sorts an array. * Sorts an array.
* This method uses 'in place' algorithm.
* @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order. * @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order.
*/ */
sort(compareFn?: (a: T, b: T) => number): T[] { sort(compareFn?: (a: T, b: T) => number): ObservableArray<T> {
return this._array.sort(compareFn); this._array.sort(compareFn);
return this;
} }
/** /**
* Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
* This method uses 'in place' algorithm.
* @param start The zero-based location in the array from which to start removing elements. * @param start The zero-based location in the array from which to start removing elements.
* @param deleteCount The number of elements to remove. * @param deleteCount The number of elements to remove.
* @param items Elements to insert into the array in place of the deleted elements. * @param items Elements to insert into the array in place of the deleted elements.
*/ */
splice(start: number, deleteCount?: number, ...items: any): T[] { splice(start: number, deleteCount?: number, ...items: any): ObservableArray<T> {
const length = this._array.length; const length = this._array.length;
const result = arguments.length === 1 ? this._array.splice(start) : this._array.splice(start, deleteCount, ...items); const result = arguments.length === 1 ? this._array.splice(start) : this._array.splice(start, deleteCount, ...items);
@ -268,7 +277,7 @@ export class ObservableArray<T> extends Observable {
this._notifyLengthChange(); this._notifyLengthChange();
} }
return result; return new ObservableArray<T>(result);
} }
/** /**
@ -289,12 +298,30 @@ export class ObservableArray<T> extends Observable {
} }
/** /**
* Returns the index of the first element in the array where predicate is true, and -1 otherwise. * Returns the first element in the array where predicate is true, and null otherwise.
* @param predicate * @param callbackfn
* @param thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead. * @param thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead.
*/ */
findIndex(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): number { find(callbackfn: (value: T, index: number, array: ObservableArray<T>) => any, thisArg?: any): number {
return this._array.findIndex(predicate, thisArg); return this._array.find((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
}
/**
* Returns the index of the first element in the array where predicate is true, and -1 otherwise.
* @param callbackfn
* @param thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead.
*/
findIndex(callbackfn: (value: T, index: number, array: ObservableArray<T>) => any, thisArg?: any): number {
return this._array.findIndex((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
}
/**
* Determines whether the specified element exists inside the array.
* @param searchElement The value to locate in the array.
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
*/
includes(searchElement: T, fromIndex?: number): boolean {
return this._array.includes(searchElement, fromIndex);
} }
/** /**
@ -303,14 +330,7 @@ export class ObservableArray<T> extends Observable {
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0. * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
*/ */
indexOf(searchElement: T, fromIndex?: number): number { indexOf(searchElement: T, fromIndex?: number): number {
const index = fromIndex ? fromIndex : 0; return this._array.indexOf(searchElement, fromIndex);
for (let i = index, l = this._array.length; i < l; i++) {
if (this._array[i] === searchElement) {
return i;
}
}
return -1;
} }
/** /**
@ -319,15 +339,7 @@ export class ObservableArray<T> extends Observable {
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array. * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
*/ */
lastIndexOf(searchElement: T, fromIndex?: number): number { lastIndexOf(searchElement: T, fromIndex?: number): number {
const index = fromIndex ? fromIndex : this._array.length - 1; return fromIndex !== undefined ? this._array.lastIndexOf(searchElement, fromIndex) : this._array.lastIndexOf(searchElement);
for (let i = index; i >= 0; i--) {
if (this._array[i] === searchElement) {
return i;
}
}
return -1;
} }
/** /**
@ -335,8 +347,8 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array. * @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/ */
every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean { every(callbackfn: (value: T, index: number, array: ObservableArray<T>) => boolean, thisArg?: any): boolean {
return this._array.every(callbackfn, thisArg); return this._array.every((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
} }
/** /**
@ -344,8 +356,8 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array. * @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/ */
some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean { some(callbackfn: (value: T, index: number, array: ObservableArray<T>) => boolean, thisArg?: any): boolean {
return this._array.some(callbackfn, thisArg); return this._array.some((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
} }
/** /**
@ -353,8 +365,8 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/ */
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void { forEach(callbackfn: (value: T, index: number, array: ObservableArray<T>) => void, thisArg?: any): void {
this._array.forEach(callbackfn, thisArg); this._array.forEach((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
} }
/** /**
@ -362,8 +374,9 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/ */
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] { map<U>(callbackfn: (value: T, index: number, array: ObservableArray<T>) => U, thisArg?: any): ObservableArray<U> {
return this._array.map(callbackfn, thisArg); const result = this._array.map((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
return new ObservableArray<U>(result);
} }
/** /**
@ -371,8 +384,9 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array. * @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/ */
filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[] { filter(callbackfn: (value: T, index: number, array: ObservableArray<T>) => boolean, thisArg?: any): ObservableArray<T> {
return this._array.filter(callbackfn, thisArg); const result = this._array.filter((value: T, index: number, array: T[]) => callbackfn(value, index, this), thisArg);
return new ObservableArray<T>(result);
} }
/** /**
@ -380,8 +394,9 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/ */
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T { reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ObservableArray<T>) => T, initialValue?: T): T {
return initialValue !== undefined ? this._array.reduce(callbackfn, initialValue) : this._array.reduce(callbackfn); const callbackWrapper = (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => callbackfn(previousValue, currentValue, currentIndex, this);
return initialValue !== undefined ? this._array.reduce(callbackWrapper, initialValue) : this._array.reduce(callbackWrapper);
} }
/** /**
@ -389,8 +404,9 @@ export class ObservableArray<T> extends Observable {
* @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/ */
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T { reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ObservableArray<T>) => T, initialValue?: T): T {
return initialValue !== undefined ? this._array.reduceRight(callbackfn, initialValue) : this._array.reduceRight(callbackfn); const callbackWrapper = (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => callbackfn(previousValue, currentValue, currentIndex, this);
return initialValue !== undefined ? this._array.reduceRight(callbackWrapper, initialValue) : this._array.reduceRight(callbackWrapper);
} }
} }