Skip to content

Commit

Permalink
feat: got the very basics working
Browse files Browse the repository at this point in the history
  • Loading branch information
CynicalBusiness committed Mar 11, 2021
1 parent c1223e3 commit 37676f9
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 4 deletions.
22 changes: 22 additions & 0 deletions src/collectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IteratorCollector } from "./types";

/**
* Collector which converts this iterator to an array, using `Array.from`.
*/
export const toArray = <TType>(): IteratorCollector<TType, TType[]> => (
input
) => Array.from(input);

/**
* Collector which converts this iterator into a single value, much like `reduce`
* @param initialValue
* @param reducer
*/
export const reduce = <TType, TResult>(
reducer: (previousValue: TResult, value: TType) => TResult,
initialValue: TResult
): IteratorCollector<TType, TResult> => (input) => {
let result = initialValue;
for (const val of input) result = reducer(result, val);
return result;
};
3 changes: 0 additions & 3 deletions src/index.test.ts

This file was deleted.

9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
"hello world!";
export * from "./types";
export * from "./query";

export * from "./operators";
export * as operators from "./operators";

export * from "./collectors";
export * as collectors from "./collectors";
25 changes: 25 additions & 0 deletions src/operators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { IteratorOperator, IteratorSingleOperator } from "./types";

// === OPERATORS
/**
* Yields values from the the previous iterator after they have been passed over the given mapping function
* @param mapper The mapping function
*/
export const map = <TInput, TOutput>(
mapper: (value: TInput) => TOutput
): IteratorOperator<TInput, TOutput> =>
function* (input) {
for (const val of input) yield mapper(val);
};

// === SINGLE OPERATORS
/**
* Yields values from the previous iterator only if they satisfy the filter function
* @param filter The filter function
*/
export const filter = <TType>(
filter: (value: TType) => boolean
): IteratorSingleOperator<TType> =>
function* (input) {
for (const val of input) if (filter(val)) yield val;
};
24 changes: 24 additions & 0 deletions src/query.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { reduce, toArray } from "./collectors";
import { filter, map } from "./operators";
import { queryOf } from "./query";

const strings = ["foo", "bar", "baz", "foobar"];

describe("query", () => {
it("pipe operators onto iterator", () => {
const arr = queryOf(strings)
.pipe(filter((v) => v.length === 3))
.pipe(map((v) => v.length))
.collect(toArray());

expect(arr).toEqual(expect.arrayContaining([3, 3, 3]));
});

it("should collect the iterator", () => {
const sl = queryOf(strings)
.pipe(filter((v) => v.length === 3))
.collect(reduce((p, v) => p + v.length, 0));

expect(sl).toBeCloseTo(9);
});
});
51 changes: 51 additions & 0 deletions src/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { QueryIterator, IteratorOperator, IteratorCollector } from "./types";

const queryProps: PropertyDescriptorMap = {
pipe: {
value(
this: IterableIterator<unknown>,
operator: IteratorOperator<unknown, unknown>
) {
return query(operator(this));
},
},
collect: {
value(
this: IterableIterator<unknown>,
collector: IteratorCollector<unknown, unknown>
) {
return collector(this);
},
},
};

/**
* Creates a query iterator from a given iterable
* @param iterable The iterable
*/
export function queryOf<TType>(
iterable: Iterable<TType>
): QueryIterator<TType> {
return query(iterable[Symbol.iterator]());
}

/**
* Creates a query iterator from a given iterator
* @param iterator The iterator
*/
export function query<TType>(iterator: Iterator<TType>): QueryIterator<TType> {
// set prototype of the iterator
const qi = Object.setPrototypeOf(
iterator,
Object.defineProperties(
Object.getPrototypeOf(iterator) ?? {},
queryProps
)
);

// make the Iterator an IterableIterator if it isn't already
if (!(Symbol.iterator in qi))
Object.defineProperty(qi, Symbol.iterator, () => iterator);

return qi;
}
29 changes: 29 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* A function which accepts the current iterator and returns a new one, pulling from the initial iterator as needed
*/
export type IteratorOperator<TInput, TOutput> = (
iterator: IterableIterator<TInput>
) => IterableIterator<TOutput>;
export type IteratorSingleOperator<TType> = IteratorOperator<TType, TType>;

/**
* A function which collects all values from an iterator into a result
*/
export type IteratorCollector<TType, TResult> = (
iterator: IterableIterator<TType>
) => TResult;

export interface QueryIterator<TType> extends IterableIterator<TType> {
/**
* Pipes this iterator over a given operator, returning the resulting iterator
* @param operator The operator to map the iterator values
*/
pipe<TOut>(operator: IteratorOperator<TType, TOut>): QueryIterator<TOut>;
// TODO multiple operators

/**
* Collects all values from the iterator into a single result
* @param collector The collector function
*/
collect<TResult>(collector: IteratorCollector<TType, TResult>): TResult;
}

0 comments on commit 37676f9

Please sign in to comment.