Initial import from old tscommon project
This commit is contained in:
commit
f2016e38e2
7 changed files with 850 additions and 0 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*.{ts,json}]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
deno.d.ts
|
||||
.vscode
|
||||
.local
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# typescript/iterators
|
||||
|
||||
[![Build Status](https://thunderk.visualstudio.com/typescript/_apis/build/status/iterators?branchName=master)](https://dev.azure.com/thunderk/typescript/_build?pipelineNameFilter=iterators)
|
326
mod.test.ts
Normal file
326
mod.test.ts
Normal file
|
@ -0,0 +1,326 @@
|
|||
import {
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
mockfn,
|
||||
patch,
|
||||
} from "https://code.thunderk.net/typescript/devtools/raw/1.3.0/testing.ts";
|
||||
import {
|
||||
ialternate,
|
||||
iarray,
|
||||
iat,
|
||||
icat,
|
||||
ichain,
|
||||
ichainit,
|
||||
icombine,
|
||||
IEMPTY,
|
||||
ifilter,
|
||||
ifilterclass,
|
||||
ifiltertype,
|
||||
ifirst,
|
||||
ifirstmap,
|
||||
iforeach,
|
||||
iloop,
|
||||
imap,
|
||||
imaterialize,
|
||||
imax,
|
||||
imin,
|
||||
ipartition,
|
||||
irange,
|
||||
irecur,
|
||||
ireduce,
|
||||
irepeat,
|
||||
isingle,
|
||||
iskip,
|
||||
istep,
|
||||
isum,
|
||||
iunique,
|
||||
izip,
|
||||
izipg,
|
||||
} from "./mod.ts";
|
||||
|
||||
class A {}
|
||||
class B {}
|
||||
|
||||
describe("Iterators", () => {
|
||||
function checkit<T>(
|
||||
base_iterator: Iterable<T>,
|
||||
values: T[],
|
||||
infinite = false,
|
||||
) {
|
||||
function checker() {
|
||||
let iterator = base_iterator[Symbol.iterator]();
|
||||
values.forEach((value) => {
|
||||
let state = iterator.next();
|
||||
expect(state.done).toBe(false);
|
||||
expect(state.value).toEqual(value);
|
||||
});
|
||||
if (!infinite) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let state = iterator.next();
|
||||
expect(state.done).toBe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make two iterations to be sure it unwinds the same
|
||||
checker();
|
||||
checker();
|
||||
}
|
||||
|
||||
it("constructs an iterator from a recurrent formula", () => {
|
||||
checkit(irecur(1, (x) => x + 2), [1, 3, 5], true);
|
||||
checkit(irecur(4, (x) => x ? x - 1 : null), [4, 3, 2, 1, 0]);
|
||||
});
|
||||
|
||||
it("constructs an iterator from an array", () => {
|
||||
checkit(iarray([]), []);
|
||||
checkit(iarray([1, 2, 3]), [1, 2, 3]);
|
||||
});
|
||||
|
||||
it("constructs an iterator from a single value", () => {
|
||||
checkit(isingle(1), [1]);
|
||||
checkit(isingle("a"), ["a"]);
|
||||
});
|
||||
|
||||
it("repeats a value", () => {
|
||||
checkit(irepeat("a"), ["a", "a", "a", "a"], true);
|
||||
checkit(irepeat("a", 3), ["a", "a", "a"]);
|
||||
});
|
||||
|
||||
it("calls a function for each yielded value", () => {
|
||||
let iterator = iarray([1, 2, 3]);
|
||||
let result: number[] = [];
|
||||
iforeach(iterator, (val) => result.push(val));
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
|
||||
result = [];
|
||||
iforeach(iterator, (i) => {
|
||||
result.push(i);
|
||||
if (i == 2) {
|
||||
return null;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
expect(result).toEqual([1, 2]);
|
||||
|
||||
result = [];
|
||||
iforeach(iterator, (i) => {
|
||||
result.push(i);
|
||||
return i;
|
||||
}, 2);
|
||||
expect(result).toEqual([1, 2]);
|
||||
|
||||
result = [];
|
||||
for (let i of iterator) {
|
||||
result.push(i);
|
||||
}
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("finds the first item passing a predicate", () => {
|
||||
expect(ifirst(iarray(<number[]> []), (i) => i % 2 == 0)).toBeNull();
|
||||
expect(ifirst(iarray([1, 2, 3]), (i) => i % 2 == 0)).toBe(2);
|
||||
expect(ifirst(iarray([1, 3, 5]), (i) => i % 2 == 0)).toBeNull();
|
||||
});
|
||||
|
||||
it("finds the first item mapping to a value", () => {
|
||||
let predicate = (i: number) => i % 2 == 0 ? (i * 4).toString() : null;
|
||||
expect(ifirstmap(iarray([]), predicate)).toBeNull();
|
||||
expect(ifirstmap(iarray([1, 2, 3]), predicate)).toBe("8");
|
||||
expect(ifirstmap(iarray([1, 3, 5]), predicate)).toBeNull();
|
||||
});
|
||||
|
||||
it("materializes an array from an iterator", () => {
|
||||
expect(imaterialize(iarray([1, 2, 3]))).toEqual([1, 2, 3]);
|
||||
|
||||
expect(() => imaterialize(iarray([1, 2, 3, 4, 5]), 2)).toThrow(
|
||||
"Length limit on iterator materialize",
|
||||
);
|
||||
});
|
||||
|
||||
it("creates an iterator in a range of integers", () => {
|
||||
checkit(irange(4), [0, 1, 2, 3]);
|
||||
checkit(irange(4, 1), [1, 2, 3, 4]);
|
||||
checkit(irange(5, 3, 2), [3, 5, 7, 9, 11]);
|
||||
checkit(irange(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true);
|
||||
});
|
||||
|
||||
it("uses a step iterator to scan numbers", () => {
|
||||
checkit(istep(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true);
|
||||
checkit(istep(3), [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], true);
|
||||
checkit(istep(3, irepeat(1, 4)), [3, 4, 5, 6, 7]);
|
||||
checkit(istep(8, IEMPTY), [8]);
|
||||
checkit(istep(1, irange()), [1, 1, 2, 4, 7, 11, 16], true);
|
||||
});
|
||||
|
||||
it("skips a number of values", () => {
|
||||
checkit(iskip(irange(7), 3), [3, 4, 5, 6]);
|
||||
checkit(iskip(irange(7), 12), []);
|
||||
checkit(iskip(IEMPTY, 3), []);
|
||||
});
|
||||
|
||||
it("gets a value at an iterator position", () => {
|
||||
expect(iat(irange(), -1)).toBeNull();
|
||||
expect(iat(irange(), 0)).toBe(0);
|
||||
expect(iat(irange(), 8)).toBe(8);
|
||||
expect(iat(irange(5), 8)).toBeNull();
|
||||
expect(iat(IEMPTY, 0)).toBeNull();
|
||||
});
|
||||
|
||||
it("chains iterator of iterators", () => {
|
||||
checkit(ichainit(IEMPTY), []);
|
||||
checkit(
|
||||
ichainit(iarray([iarray([1, 2, 3]), iarray([]), iarray([4, 5])])),
|
||||
[1, 2, 3, 4, 5],
|
||||
);
|
||||
});
|
||||
|
||||
it("chains iterators", () => {
|
||||
checkit(ichain(), []);
|
||||
checkit(ichain(irange(3)), [0, 1, 2]);
|
||||
checkit(ichain(iarray([1, 2]), iarray([]), iarray([3, 4, 5])), [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
});
|
||||
|
||||
it("loops an iterator", () => {
|
||||
checkit(iloop(irange(3), 2), [0, 1, 2, 0, 1, 2]);
|
||||
checkit(
|
||||
iloop(irange(1)),
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
true,
|
||||
);
|
||||
|
||||
let onloop = mockfn();
|
||||
let iterator = iloop(irange(2), 3, onloop)[Symbol.iterator]();
|
||||
|
||||
expect(iterator.next().value).toBe(0);
|
||||
expect(onloop).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(iterator.next().value).toBe(1);
|
||||
expect(onloop).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(iterator.next().value).toBe(0);
|
||||
expect(onloop).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(iterator.next().value).toBe(1);
|
||||
expect(onloop).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(iterator.next().value).toBe(0);
|
||||
expect(onloop).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(iterator.next().value).toBe(1);
|
||||
expect(onloop).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(iterator.next().value).toBeUndefined();
|
||||
expect(onloop).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("maps an iterator", () => {
|
||||
checkit(imap(IEMPTY, (i) => i * 2), []);
|
||||
checkit(imap(irange(3), (i) => i * 2), [0, 2, 4]);
|
||||
});
|
||||
|
||||
it("reduces an iterator", () => {
|
||||
expect(ireduce(IEMPTY, (a, b) => a + b, 2)).toBe(2);
|
||||
expect(ireduce([9], (a, b) => a + b, 2)).toBe(11);
|
||||
expect(ireduce([9, 1], (a, b) => a + b, 2)).toBe(12);
|
||||
});
|
||||
|
||||
it("filters an iterator with a predicate", () => {
|
||||
checkit(imap(IEMPTY, (i) => i % 3 == 0), []);
|
||||
checkit(ifilter(irange(12), (i) => i % 3 == 0), [0, 3, 6, 9]);
|
||||
});
|
||||
|
||||
it("filters an iterator with a type guard", () => {
|
||||
let result = ifiltertype(
|
||||
<(number | string)[]> [1, "a", 2, "b"],
|
||||
(x): x is number => typeof x == "number",
|
||||
);
|
||||
checkit(result, [1, 2]);
|
||||
});
|
||||
|
||||
it("filters an iterator with a class type", () => {
|
||||
let o1 = new A();
|
||||
let o2 = new A();
|
||||
let o3 = new B();
|
||||
let result = ifilterclass([1, "a", o1, 2, o2, o3, "b"], A);
|
||||
checkit(result, [o1, o2]);
|
||||
});
|
||||
|
||||
it("combines iterators", () => {
|
||||
let iterator = icombine(iarray([1, 2, 3]), iarray(["a", "b"]));
|
||||
checkit(iterator, [
|
||||
[1, "a"],
|
||||
[1, "b"],
|
||||
[2, "a"],
|
||||
[2, "b"],
|
||||
[3, "a"],
|
||||
[3, "b"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("zips iterators", () => {
|
||||
checkit(izip(IEMPTY, IEMPTY), []);
|
||||
checkit(izip(iarray([1, 2, 3]), iarray(["a", "b"])), [[1, "a"], [
|
||||
2,
|
||||
"b",
|
||||
]]);
|
||||
|
||||
checkit(izipg(IEMPTY, IEMPTY), []);
|
||||
checkit(
|
||||
izipg(iarray([1, 2, 3]), iarray(["a", "b"])),
|
||||
<[number | undefined, string | undefined][]> [[1, "a"], [2, "b"], [
|
||||
3,
|
||||
undefined,
|
||||
]],
|
||||
);
|
||||
});
|
||||
|
||||
it("partitions iterators", () => {
|
||||
let [it1, it2] = ipartition(IEMPTY, () => true);
|
||||
checkit(it1, []);
|
||||
checkit(it2, []);
|
||||
|
||||
[it1, it2] = ipartition(irange(5), (i) => i % 2 == 0);
|
||||
checkit(it1, [0, 2, 4]);
|
||||
checkit(it2, [1, 3]);
|
||||
});
|
||||
|
||||
it("alternatively pick from several iterables", () => {
|
||||
checkit(ialternate([]), []);
|
||||
checkit(
|
||||
ialternate([[1, 2, 3, 4], [], iarray([5, 6]), IEMPTY, iarray([7, 8, 9])]),
|
||||
[1, 5, 7, 2, 6, 8, 3, 9, 4],
|
||||
);
|
||||
});
|
||||
|
||||
it("returns unique items", () => {
|
||||
checkit(iunique(IEMPTY), []);
|
||||
checkit(iunique(iarray([5, 3, 2, 3, 4, 5])), [5, 3, 2, 4]);
|
||||
checkit(iunique(iarray([5, 3, 2, 3, 4, 5]), 4), [5, 3, 2, 4]);
|
||||
expect(() => imaterialize(iunique(iarray([5, 3, 2, 3, 4, 5]), 3))).toThrow(
|
||||
"Unique count limit on iterator",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses ireduce for some common functions", () => {
|
||||
expect(isum(IEMPTY)).toEqual(0);
|
||||
expect(isum(irange(4))).toEqual(6);
|
||||
|
||||
expect(icat(IEMPTY)).toEqual("");
|
||||
expect(icat(iarray(["a", "bc", "d"]))).toEqual("abcd");
|
||||
|
||||
expect(imin(IEMPTY)).toEqual(Infinity);
|
||||
expect(imin(iarray([3, 8, 2, 4]))).toEqual(2);
|
||||
|
||||
expect(imax(IEMPTY)).toEqual(-Infinity);
|
||||
expect(imax(iarray([3, 8, 2, 4]))).toEqual(8);
|
||||
});
|
||||
});
|
480
mod.ts
Normal file
480
mod.ts
Normal file
|
@ -0,0 +1,480 @@
|
|||
/**
|
||||
* Empty iterator
|
||||
*/
|
||||
export const IATEND: Iterator<any> = {
|
||||
next: function () {
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Empty iterable
|
||||
*/
|
||||
export const IEMPTY: Iterable<any> = {
|
||||
[Symbol.iterator]: () => IATEND,
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterable constructor, from an initial value, and a step value
|
||||
*/
|
||||
export function irecur<T, S>(start: T, step: (a: T) => T | null): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let val: T | null = start;
|
||||
do {
|
||||
yield val;
|
||||
val = step(val);
|
||||
} while (val !== null);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable constructor, from an array
|
||||
*
|
||||
* The iterator will yield the next value each time it is called, then undefined when the array's end is reached.
|
||||
*/
|
||||
export function iarray<T>(array: T[], offset = 0): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function () {
|
||||
return array.slice(offset)[Symbol.iterator]();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable constructor, from a single value
|
||||
*
|
||||
* The value will be yielded only once, not repeated over.
|
||||
*/
|
||||
export function isingle<T>(value: T): Iterable<T> {
|
||||
return iarray([value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable that repeats the same value.
|
||||
*/
|
||||
export function irepeat<T>(value: T, count = -1): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let n = count;
|
||||
while (n != 0) {
|
||||
yield value;
|
||||
n--;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of Array.forEach for all iterables.
|
||||
*
|
||||
* If the callback returns *stopper*, the iteration is stopped.
|
||||
*/
|
||||
export function iforeach<T>(
|
||||
iterable: Iterable<T>,
|
||||
callback: (_: T) => any,
|
||||
stopper: any = null,
|
||||
): void {
|
||||
for (let value of iterable) {
|
||||
if (callback(value) === stopper) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first item passing a predicate
|
||||
*/
|
||||
export function ifirst<T>(
|
||||
iterable: Iterable<T>,
|
||||
predicate: (item: T) => boolean,
|
||||
): T | null {
|
||||
for (let value of iterable) {
|
||||
if (predicate(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-null result of a value-yielding predicate, applied to each iterator element
|
||||
*/
|
||||
export function ifirstmap<T1, T2>(
|
||||
iterable: Iterable<T1>,
|
||||
predicate: (item: T1) => T2 | null,
|
||||
): T2 | null {
|
||||
for (let value of iterable) {
|
||||
let res = predicate(value);
|
||||
if (res !== null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Materialize an array from consuming an iterable
|
||||
*
|
||||
* To avoid materializing infinite iterators (and bursting memory), the item count is limited to 1 million, and an
|
||||
* exception is thrown when this limit is reached.
|
||||
*/
|
||||
export function imaterialize<T>(iterable: Iterable<T>, limit = 1000000): T[] {
|
||||
let result: T[] = [];
|
||||
|
||||
for (let value of iterable) {
|
||||
result.push(value);
|
||||
if (result.length >= limit) {
|
||||
throw new Error("Length limit on iterator materialize");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over natural integers
|
||||
*
|
||||
* If *count* is not specified, the iterator is infinite
|
||||
*/
|
||||
export function irange(
|
||||
count: number = -1,
|
||||
start = 0,
|
||||
step = 1,
|
||||
): Iterable<number> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let i = start;
|
||||
let n = count;
|
||||
while (n != 0) {
|
||||
yield i;
|
||||
i += step;
|
||||
n--;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over numbers, by applying a step taken from an other iterator
|
||||
*
|
||||
* This iterator stops when the "step iterator" stops
|
||||
*
|
||||
* With no argument, istep() == irange()
|
||||
*/
|
||||
export function istep(start = 0, step_iterable = irepeat(1)): Iterable<number> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let i = start;
|
||||
yield i;
|
||||
for (let step of step_iterable) {
|
||||
i += step;
|
||||
yield i;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip a given number of values from an iterator, discarding them.
|
||||
*/
|
||||
export function iskip<T>(iterable: Iterable<T>, count = 1): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function () {
|
||||
let iterator = iterable[Symbol.iterator]();
|
||||
let n = count;
|
||||
while (n-- > 0) {
|
||||
iterator.next();
|
||||
}
|
||||
return iterator;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value at a given position in the iterator
|
||||
*/
|
||||
export function iat<T>(iterable: Iterable<T>, position: number): T | null {
|
||||
if (position < 0) {
|
||||
return null;
|
||||
} else {
|
||||
if (position > 0) {
|
||||
iterable = iskip(iterable, position);
|
||||
}
|
||||
let iterator = iterable[Symbol.iterator]();
|
||||
let state = iterator.next();
|
||||
return state.done ? null : state.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chain an iterable of iterables.
|
||||
*
|
||||
* This will yield values from the first yielded iterator, then the second one, and so on...
|
||||
*/
|
||||
export function ichainit<T>(iterables: Iterable<Iterable<T>>): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
for (let iterable of iterables) {
|
||||
for (let value of iterable) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Chain iterables.
|
||||
*
|
||||
* This will yield values from the first iterator, then the second one, and so on...
|
||||
*/
|
||||
export function ichain<T>(...iterables: Iterable<T>[]): Iterable<T> {
|
||||
if (iterables.length == 0) {
|
||||
return IEMPTY;
|
||||
} else {
|
||||
return ichainit(iterables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop an iterator for a number of times.
|
||||
*
|
||||
* If count is negative, if will loop forever (infinite iterator).
|
||||
*
|
||||
* onloop may be used to know when the iterator resets.
|
||||
*/
|
||||
export function iloop<T>(
|
||||
base: Iterable<T>,
|
||||
count = -1,
|
||||
onloop?: Function,
|
||||
): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let n = count;
|
||||
let start = false;
|
||||
while (n-- != 0) {
|
||||
for (let value of base) {
|
||||
if (start) {
|
||||
if (onloop) {
|
||||
onloop();
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
start = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator version of "map".
|
||||
*/
|
||||
export function imap<T1, T2>(
|
||||
iterable: Iterable<T1>,
|
||||
mapfunc: (_: T1) => T2,
|
||||
): Iterable<T2> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
for (let value of iterable) {
|
||||
yield mapfunc(value);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator version of "reduce".
|
||||
*/
|
||||
export function ireduce<T>(
|
||||
iterable: Iterable<T>,
|
||||
reduce: (item1: T, item2: T) => T,
|
||||
init: T,
|
||||
): T {
|
||||
let result = init;
|
||||
for (let value of iterable) {
|
||||
result = reduce(result, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator version of "filter".
|
||||
*/
|
||||
export function ifilter<T>(
|
||||
iterable: Iterable<T>,
|
||||
filterfunc: (_: T) => boolean,
|
||||
): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
for (let value of iterable) {
|
||||
if (filterfunc(value)) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Type filter, to return a list of instances of a given type
|
||||
*/
|
||||
export function ifiltertype<T>(
|
||||
iterable: Iterable<any>,
|
||||
filter: (item: any) => item is T,
|
||||
): Iterable<T> {
|
||||
return ifilter(iterable, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class filter, to return a list of instances of a given type
|
||||
*/
|
||||
export function ifilterclass<T>(
|
||||
iterable: Iterable<any>,
|
||||
classref: { new (...args: any[]): T },
|
||||
): Iterable<T> {
|
||||
return ifilter(iterable, (item): item is T => item instanceof classref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two iterables.
|
||||
*
|
||||
* This iterates through the second one several times, so if one iterator may be infinite,
|
||||
* it should be the first one.
|
||||
*/
|
||||
export function icombine<T1, T2>(
|
||||
it1: Iterable<T1>,
|
||||
it2: Iterable<T2>,
|
||||
): Iterable<[T1, T2]> {
|
||||
return ichainit(imap(it1, (v1) => imap(it2, (v2): [T1, T2] => [v1, v2])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance through two iterables at the same time, yielding item pairs
|
||||
*
|
||||
* Iteration will stop at the first of the two iterators that stops.
|
||||
*/
|
||||
export function izip<T1, T2>(
|
||||
it1: Iterable<T1>,
|
||||
it2: Iterable<T2>,
|
||||
): Iterable<[T1, T2]> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let iterator1 = it1[Symbol.iterator]();
|
||||
let iterator2 = it2[Symbol.iterator]();
|
||||
let state1 = iterator1.next();
|
||||
let state2 = iterator2.next();
|
||||
while (!state1.done && !state2.done) {
|
||||
yield [state1.value, state2.value];
|
||||
state1 = iterator1.next();
|
||||
state2 = iterator2.next();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance two iterables at the same time, yielding item pairs (greedy version)
|
||||
*
|
||||
* Iteration will stop when both iterators are consumed, returning partial couples (undefined in the peer) if needed.
|
||||
*/
|
||||
export function izipg<T1, T2>(
|
||||
it1: Iterable<T1>,
|
||||
it2: Iterable<T2>,
|
||||
): Iterable<[T1 | undefined, T2 | undefined]> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let iterator1 = it1[Symbol.iterator]();
|
||||
let iterator2 = it2[Symbol.iterator]();
|
||||
let state1 = iterator1.next();
|
||||
let state2 = iterator2.next();
|
||||
while (!state1.done || !state2.done) {
|
||||
yield [state1.value, state2.value];
|
||||
state1 = iterator1.next();
|
||||
state2 = iterator2.next();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Partition in two iterables, one with values that pass the predicate, the other with values that don't
|
||||
*/
|
||||
export function ipartition<T>(
|
||||
iterable: Iterable<T>,
|
||||
predicate: (item: T) => boolean,
|
||||
): [Iterable<T>, Iterable<T>] {
|
||||
return [
|
||||
ifilter(iterable, predicate),
|
||||
ifilter(iterable, (x) => !predicate(x)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate between several iterables (pick one from the first one, then one from the second...)
|
||||
*/
|
||||
export function ialternate<T>(iterables: Iterable<T>[]): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let iterators = iterables.map((iterable) => iterable[Symbol.iterator]());
|
||||
let done: boolean;
|
||||
do {
|
||||
done = false;
|
||||
// TODO Remove "dried-out" iterators
|
||||
for (let iterator of iterators) {
|
||||
let state = iterator.next();
|
||||
if (!state.done) {
|
||||
done = true;
|
||||
yield state.value;
|
||||
}
|
||||
}
|
||||
} while (done);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Yield items from an iterator only once.
|
||||
*
|
||||
* Beware that even if this function is not materializing, it keeps track of yielded item, and may choke on
|
||||
* infinite or very long streams. Thus, no more than *limit* items will be yielded (an error is thrown
|
||||
* when this limit is reached).
|
||||
*
|
||||
* This function is O(n²)
|
||||
*/
|
||||
export function iunique<T>(
|
||||
iterable: Iterable<T>,
|
||||
limit = 1000000,
|
||||
): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let done: T[] = [];
|
||||
let n = limit;
|
||||
for (let value of iterable) {
|
||||
if (done.indexOf(value) < 0) {
|
||||
if (n-- > 0) {
|
||||
done.push(value);
|
||||
yield value;
|
||||
} else {
|
||||
throw new Error("Unique count limit on iterator");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Common reduce shortcuts
|
||||
*/
|
||||
export const isum = (iterable: Iterable<number>) =>
|
||||
ireduce(iterable, (a, b) => a + b, 0);
|
||||
export const icat = (iterable: Iterable<string>) =>
|
||||
ireduce(iterable, (a, b) => a + b, "");
|
||||
export const imin = (iterable: Iterable<number>) =>
|
||||
ireduce(iterable, Math.min, Infinity);
|
||||
export const imax = (iterable: Iterable<number>) =>
|
||||
ireduce(iterable, Math.max, -Infinity);
|
19
run
Executable file
19
run
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
# Simplified run tool for deno commands
|
||||
|
||||
if test $# -eq 0
|
||||
then
|
||||
echo "Usage: $0 [file or command]"
|
||||
exit 1
|
||||
elif echo $1 | grep -q '.*.ts'
|
||||
then
|
||||
denocmd=run
|
||||
denoargs=$1
|
||||
shift
|
||||
else
|
||||
denocmd=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
denoargs="$(cat config/$denocmd.flags 2> /dev/null) $denoargs $@"
|
||||
exec deno $denocmd $denoargs
|
10
tsconfig.json
Normal file
10
tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"preserveConstEnums": true
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue