Skip to content

Commit

Permalink
Added forEach implementation & extended every
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrei15193 committed May 3, 2024
1 parent bbb6472 commit c1c43a7
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 1 deletion.
35 changes: 35 additions & 0 deletions src/collections/IObservableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,41 @@ export interface IObservableCollection<TItem> extends IReadOnlyObservableCollect
*/
length: number;

/**
* Checks whether all elements in the collection satisfy a given condition.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every(predicate: (item: TItem, index: number, collection: this) => boolean): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TSpecific extends TItem>(predicate: (item: TItem, index: number, collection: this) => item is TSpecific): this is IObservableCollection<TSpecific>;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg: TContext): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TSpecific extends TItem, TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TSpecific, thisArg: TContext): this is IObservableCollection<TSpecific>;

/**
* Appends new elements to the end of the collection, and returns the new length of the collection.
* @param items New elements to add at the end of the collection.
Expand Down
18 changes: 18 additions & 0 deletions src/collections/IReadOnlyObservableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ export interface IReadOnlyObservableCollection<TItem> extends Iterable<TItem>, I
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every(predicate: (item: TItem, index: number, collection: this) => boolean): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TSpecific extends TItem>(predicate: (item: TItem, index: number, collection: this) => item is TSpecific): this is IReadOnlyObservableCollection<TSpecific>;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TContext The context type in which the callback is executed.
Expand All @@ -204,6 +212,16 @@ export interface IReadOnlyObservableCollection<TItem> extends Iterable<TItem>, I
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg: TContext): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
every<TSpecific extends TItem, TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TSpecific, thisArg: TContext): this is IReadOnlyObservableCollection<TSpecific>;

reduce(callback: (accumulator: TItem, item: TItem, index: number, colleciton: this) => TItem): TItem;
reduce<TResult>(callback: (accumulator: TResult, item: TItem, index: number, colleciton: this) => TItem, initialValue: TResult): TItem;
Expand Down
39 changes: 39 additions & 0 deletions src/collections/ObservableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,45 @@ export class ObservableCollection<TItem> extends ReadOnlyObservableCollection<TI
super(...items);
}

/**
* Checks whether all elements in the collection satisfy a given condition.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every(predicate: (item: TItem, index: number, collection: this) => boolean): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TSpecific extends TItem>(predicate: (item: TItem, index: number, collection: this) => item is TSpecific): this is ObservableCollection<TSpecific>;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg: TContext): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TSpecific extends TItem, TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TSpecific, thisArg: TContext): this is ObservableCollection<TSpecific>;

public every<TContext = void>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean {
return super.every(predicate, thisArg);
}

/**
* Gets or sets the number of items in the collection.
* @see [Array.length](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/length)
Expand Down
21 changes: 20 additions & 1 deletion src/collections/ReadOnlyObservableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ export class ReadOnlyObservableCollection<TItem> extends ViewModel implements IR
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every(predicate: (item: TItem, index: number, collection: this) => boolean): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @param predicate The callback performing the check for each item.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TSpecific extends TItem>(predicate: (item: TItem, index: number, collection: this) => item is TSpecific): this is ReadOnlyObservableCollection<TSpecific>;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TContext The context type in which the callback is executed.
Expand All @@ -345,6 +353,16 @@ export class ReadOnlyObservableCollection<TItem> extends ViewModel implements IR
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg: TContext): boolean;
/**
* Checks whether all elements in the collection satisfy a given condition.
* @template TSpecific The specific item type the collection actually contains.
* @template TContext The context type in which the callback is executed.
* @param predicate The callback performing the check for each item.
* @param thisArg A value to use as context when checking items.
* @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`.
* @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
*/
public every<TSpecific extends TItem, TContext>(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TSpecific, thisArg: TContext): this is ReadOnlyObservableCollection<TSpecific>;

public every<TContext = void>(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean {
let result = true;
Expand Down Expand Up @@ -403,7 +421,8 @@ export class ReadOnlyObservableCollection<TItem> extends ViewModel implements IR
public forEach<TContext>(callbackfn: (this: TContext, item: TItem, index: number, collection: this) => void, thisArg: TContext): void;

public forEach<TContext = void>(callbackfn: (this: TContext, item: TItem, index: number, collection: this) => void, thisArg?: TContext): void {
throw new Error('Method not implemented.');
for (let index = 0; index < this._length; index++)
callbackfn.call(thisArg, this[index], index, this);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('ObserableCollection.every', (): void => {
it('calling every passes arguments to each parameter accordingly', (): void => {
let invocationCount = 0;
const observableCollection = new ObservableCollection<number>(1);

observableCollection.every((item, index, collection) => {
invocationCount++;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ObservableCollection } from '../../ObservableCollection';
import { testBlankMutatingOperation } from './common';

describe('ObserableCollection.forEach', (): void => {
it('iterating over an empty collection does not invoke the callback', (): void => {
let arrayInvocationCount = 0;
let collectionInvocationCount = 0;

testBlankMutatingOperation<number>({
initialState: [],

applyOperation: {
applyArrayOperation: array => array.forEach(_ => arrayInvocationCount++),
applyCollectionOperation: collection => collection.forEach(_ => collectionInvocationCount++)
}
});

expect(arrayInvocationCount).toBe(0);
expect(collectionInvocationCount).toBe(0);
});

it('iterating over a collection invokes the callback for each item', (): void => {
const arrayItems: number[] = [];
const collectionItems: number[] = [];

testBlankMutatingOperation<number>({
initialState: [1, 2, 3],

applyOperation: {
applyArrayOperation: array => array.forEach(item => arrayItems.push(item)),
applyCollectionOperation: collection => collection.forEach(item => collectionItems.push(item))
}
});

expect(collectionItems).toEqual(arrayItems);
});

it('calling forEach passes arguments to each parameter accordingly', (): void => {
let invocationCount = 0;
const observableCollection = new ObservableCollection<number>(1);
observableCollection.forEach((item, index, collection) => {
invocationCount++;

expect(item).toBe(1);
expect(index).toBe(0);
expect(collection).toStrictEqual(observableCollection);

return true;
});

expect(invocationCount).toBe(1);
});

it('calling forEach with context passes it to the callback', (): void => {
let invocationCount = 0;
const context = {};
const observableCollection = new ObservableCollection<number>(1);
observableCollection.forEach(
function (item, index, collection) {
invocationCount++;

expect(this).toStrictEqual(context);
expect(item).toBe(1);
expect(index).toBe(0);
expect(collection).toStrictEqual(observableCollection);

return true;
},
context
);

expect(invocationCount).toBe(1);
});

it('calling forEach while iterating will not break iterators', (): void => {
expect(
() => {
const observableCollection = new ObservableCollection<number>(1, 2, 3);

for (const _ of observableCollection)
observableCollection.forEach(_ => {});
})
.not
.toThrow();
});
});

0 comments on commit c1c43a7

Please sign in to comment.