Skip to content

Commit

Permalink
feat(without): add without support to fromfrom
Browse files Browse the repository at this point in the history
  • Loading branch information
jtenner authored and Tomi Turtiainen committed Apr 2, 2019
1 parent 84ce397 commit 86f9e51
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 11 deletions.
41 changes: 30 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions src/Sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ReduceCallbackFn,
NumberKeyedObject,
StringKeyedObject,
ComparePredicate,
} from "./types";
import { createConcatIterable } from "./transforms/concat";
import { createDistinctIterable } from "./transforms/distinct";
Expand All @@ -21,6 +22,7 @@ import { createTakeIterable } from "./transforms/take";
import { createSortByIterable } from "./transforms/sortBy";
import { createTakeWhileIterable } from "./transforms/takeWhile";
import { createSkipWhileIterable } from "./transforms/skipWhile";
import { createWithoutIterable } from "./transforms/without";

const identityPredicateFn = (x: any): boolean => x;

Expand Down Expand Up @@ -526,6 +528,34 @@ export class Sequence<TItem> implements Iterable<TItem> {
return new Sequence(createTakeWhileIterable(this._iterable, predicate));
}

/**
* Returns elements from a sequence as long as they don't exist in the specified iterable items.
*
* @param items The provided set of items that should not be in the returned Sequence.
* @param predicate The optional predicate that determines if two TItem items are equal.
*
* @example
* ```ts
* // returns [2, 4, 6]
* from([1, 2, 3, 4, 5, 6])
* .without([1, 3, 5])
* .toArray();
*
* // returns [{ id: 1 }, { id: 3 }]
* from([{ id: 1 }, { id: 2 }, { id: 3 }])
* .without([{ id: 2 }], (a, b) => a.id === b.id)
* .toArray();
* ```
*/
without(
items: Iterable<TItem>,
predicate?: ComparePredicate<TItem>
): Sequence<TItem> {
return new Sequence(
createWithoutIterable(this._iterable, items, predicate)
);
}

/**
* Converts the sequence to an array
*
Expand Down
39 changes: 39 additions & 0 deletions src/transforms/without.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ComparePredicate } from "../types";
import { createFilterIterable } from "./filter";
import { IterableCreatorIterable } from "../IterableCreatorIterable";

export const createWithoutIterable = <TItem>(
iterator: Iterable<TItem>,
withoutItems: Iterable<TItem>,
comparePredicate?: ComparePredicate<TItem>
): Iterable<TItem> => {
if (!comparePredicate) {
const withoutSet = new Set(withoutItems);
// fast path, create a filter iterable with the `Set.prototype.has` function call
return createFilterIterable(iterator, item => !withoutSet.has(item));
} else {
// Must compare each item for equality for each item
return new IterableCreatorIterable(function* filter(): IterableIterator<
TItem
> {
// cache already found results
const cache = new Set();

outer: for (const item of iterator) {
// fast path, this item was already found, don't loop
if (cache.has(item)) continue;
// slow path, loop over each item in the set, determine if it matches the ComparePredicate
for (const withoutItem of withoutItems) {
// if the item is found, add it to the cache and skip the item
if (comparePredicate(item, withoutItem)) {
cache.add(item);
continue outer;
}
}

// we can safely yield the item
yield item;
}
});
}
};
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type ComparerFn<TItem> = (a: TItem, b: TItem) => number;

export type IteratorCreatorFn<TResult> = () => Iterator<TResult>;

export type ComparePredicate<TItem> = (a: TItem, b: TItem) => boolean;

export interface Grouping<TKey, TElement> {
key: TKey;
items: TElement[];
Expand Down
31 changes: 31 additions & 0 deletions test/fromfrom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,37 @@ describe("fromfrom", () => {
});
});

describe("without", () => {
const numbers = [1, 2, 3, 4, 5, 6];
it("should return a sequence without values", () => {
const sequence = from(numbers).without([1, 3, 5]);

expect(Array.from(sequence)).toStrictEqual([2, 4, 6]);
});

it("should use a predicate function", () => {
const items = [
{ id: 0, name: "John", age: 20 },
{ id: 1, name: "Tony", age: 30 },
{ id: 2, name: "Mark", age: 30 },
{ id: 3, name: "Jane", age: 20 },
{ id: 4, name: "Lisa", age: 30 },
];
const without = [
{ id: 0, name: "", age: 0 },
{ id: 1, name: "", age: 0 },
{ id: 2, name: "", age: 0 },
];

const sequence = from(items).without(without, (a, b) => a.id === b.id);

expect(Array.from(sequence)).toStrictEqual([
{ id: 3, name: "Jane", age: 20 },
{ id: 4, name: "Lisa", age: 30 },
]);
});
});

describe("toArray", () => {
it("returns an array", () => {
expect(from([1, 2]).toArray()).toEqual([1, 2]);
Expand Down

0 comments on commit 86f9e51

Please sign in to comment.