Add ilength and some documentation
This commit is contained in:
parent
f2016e38e2
commit
db54d4f444
79
README.md
79
README.md
|
@ -1,3 +1,82 @@
|
||||||
# typescript/iterators
|
# typescript/iterators
|
||||||
|
|
||||||
[![Build Status](https://thunderk.visualstudio.com/typescript/_apis/build/status/iterators?branchName=master)](https://dev.azure.com/thunderk/typescript/_build?pipelineNameFilter=iterators)
|
[![Build Status](https://thunderk.visualstudio.com/typescript/_apis/build/status/iterators?branchName=master)](https://dev.azure.com/thunderk/typescript/_build?pipelineNameFilter=iterators)
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Lazy iterators to work on dynamic data sets without materializing them.
|
||||||
|
|
||||||
|
They allow to work on infinite streams of values, with limited memory
|
||||||
|
consumption.
|
||||||
|
|
||||||
|
Functions in this library that do not return an Iterable are "materializing",
|
||||||
|
meaning that they may consume iterators up to the end, and will not work well on
|
||||||
|
infinite iterators (a limit is often provided to break out).
|
||||||
|
|
||||||
|
These iterators are guaranteed to be repeatable, meaning that calling
|
||||||
|
Symbol.iterator on them will start over.
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
In deno:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { iarray, isum } from "https://js.thunderk.net/iterators/mod.ts";
|
||||||
|
```
|
||||||
|
|
||||||
|
In browser:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import { iarray, isum } from "https://js.thunderk.net/iterators/mod.js";
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
Examples (edge cases can be found in each function's documentation):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Create iterables
|
||||||
|
const i1 = iarray([1, 2, 3]); // 1 2 3
|
||||||
|
const i2 = isingle(4); // 4
|
||||||
|
const i3 = irecur(0, (x) => x - 1); // 0 -1 -2 -3 -4 ...
|
||||||
|
const i4 = irange(5); // 0 1 2 3 4
|
||||||
|
const i5 = irange(5, 1, 2); // 1 3 5 7 9
|
||||||
|
const i6 = istep(4, iarray([1, 10, 1, -1])); // 4 5 15 16 15
|
||||||
|
const i7 = irepeat(2); // 2 2 2 2 2 ...
|
||||||
|
|
||||||
|
// Consume iterables
|
||||||
|
iforeach(i1, console.log);
|
||||||
|
ifirst(i6); // 4
|
||||||
|
ifirstmap(i1, (x) => x > 1 ? -x : null); // -2
|
||||||
|
imaterialize(i1); // [1, 2, 3]
|
||||||
|
ilength(i1); // 3
|
||||||
|
iat(i5, 3); // 7
|
||||||
|
ireduce(i1, (a, b) => a + b, 0); // 6
|
||||||
|
isum(i1); // 6
|
||||||
|
icat(iarray(["a", "b", "c"])); // abc
|
||||||
|
imin(i1); // 1
|
||||||
|
imax(i1); // 3
|
||||||
|
|
||||||
|
// Transform iterables
|
||||||
|
iloop(i1); // 1 2 3 1 2 3 ...
|
||||||
|
iskip(i3, 2); // -2 -3 -4 -5 -6 ...
|
||||||
|
imap(i1, (x) => x * 2); // 2 4 6
|
||||||
|
ifilter(i3, (x) => x % 4 == 0); // 0 -4 -8 -12 ...
|
||||||
|
ipartition(i3, (x) => x % 4 == 0); // [0 -4 -8 ..., -1 -2 -3 -5 ...]
|
||||||
|
iunique(iarray([1, 4, 2, 4, 3, 1, 5])); // 1 4 2 3 5
|
||||||
|
|
||||||
|
// Combine iterables
|
||||||
|
ichain(i1, i2); // 1 2 3 4
|
||||||
|
ichainit(iarray([i1, i2])); // 1 2 3 4
|
||||||
|
icombine(iarray([0, 1]), iarray(["a", "b"])); // [0, "a"] [0, "b"] [1, "a"] [2, "b"]
|
||||||
|
izip(iarray([0, 1]), iarray(["a", "b"])); // [0, "a"] [1, "b"]
|
||||||
|
ialternate(iarray([0, 1]), iarray(["a", "b"])); // 0 "a" 1 "b"
|
||||||
|
|
||||||
|
// Type filter (result is a typed iterable)
|
||||||
|
ifiltertype(iarray([1, "a", 2, "b"]), (x): x is number => typeof x == "number"); // 1 2
|
||||||
|
class A {}
|
||||||
|
class B {}
|
||||||
|
ifilterclass(iarray([new A(), new B(), new A()]), A); // A A
|
||||||
|
```
|
||||||
|
|
6
deps.testing.ts
Normal file
6
deps.testing.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export {
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
mockfn,
|
||||||
|
} from "https://js.thunderk.net/devtools@1.3.0/testing.ts";
|
13
doc/about.md
Normal file
13
doc/about.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
## About
|
||||||
|
|
||||||
|
Lazy iterators to work on dynamic data sets without materializing them.
|
||||||
|
|
||||||
|
They allow to work on infinite streams of values, with limited memory
|
||||||
|
consumption.
|
||||||
|
|
||||||
|
Functions in this library that do not return an Iterable are "materializing",
|
||||||
|
meaning that they may consume iterators up to the end, and will not work well on
|
||||||
|
infinite iterators (a limit is often provided to break out).
|
||||||
|
|
||||||
|
These iterators are guaranteed to be repeatable, meaning that calling
|
||||||
|
Symbol.iterator on them will start over.
|
15
doc/import.md
Normal file
15
doc/import.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
## Import
|
||||||
|
|
||||||
|
In deno:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { iarray, isum } from "https://js.thunderk.net/iterators/mod.ts";
|
||||||
|
```
|
||||||
|
|
||||||
|
In browser:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import { iarray, isum } from "https://js.thunderk.net/iterators/mod.js";
|
||||||
|
</script>
|
||||||
|
```
|
48
doc/use.md
Normal file
48
doc/use.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
## Use
|
||||||
|
|
||||||
|
Examples (edge cases can be found in each function's documentation):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Create iterables
|
||||||
|
const i1 = iarray([1, 2, 3]); // 1 2 3
|
||||||
|
const i2 = isingle(4); // 4
|
||||||
|
const i3 = irecur(0, (x) => x - 1); // 0 -1 -2 -3 -4 ...
|
||||||
|
const i4 = irange(5); // 0 1 2 3 4
|
||||||
|
const i5 = irange(5, 1, 2); // 1 3 5 7 9
|
||||||
|
const i6 = istep(4, iarray([1, 10, 1, -1])); // 4 5 15 16 15
|
||||||
|
const i7 = irepeat(2); // 2 2 2 2 2 ...
|
||||||
|
|
||||||
|
// Consume iterables
|
||||||
|
iforeach(i1, console.log);
|
||||||
|
ifirst(i6); // 4
|
||||||
|
ifirstmap(i1, (x) => x > 1 ? -x : null); // -2
|
||||||
|
imaterialize(i1); // [1, 2, 3]
|
||||||
|
ilength(i1); // 3
|
||||||
|
iat(i5, 3); // 7
|
||||||
|
ireduce(i1, (a, b) => a + b, 0); // 6
|
||||||
|
isum(i1); // 6
|
||||||
|
icat(iarray(["a", "b", "c"])); // abc
|
||||||
|
imin(i1); // 1
|
||||||
|
imax(i1); // 3
|
||||||
|
|
||||||
|
// Transform iterables
|
||||||
|
iloop(i1); // 1 2 3 1 2 3 ...
|
||||||
|
iskip(i3, 2); // -2 -3 -4 -5 -6 ...
|
||||||
|
imap(i1, (x) => x * 2); // 2 4 6
|
||||||
|
ifilter(i3, (x) => x % 4 == 0); // 0 -4 -8 -12 ...
|
||||||
|
ipartition(i3, (x) => x % 4 == 0); // [0 -4 -8 ..., -1 -2 -3 -5 ...]
|
||||||
|
iunique(iarray([1, 4, 2, 4, 3, 1, 5])); // 1 4 2 3 5
|
||||||
|
|
||||||
|
// Combine iterables
|
||||||
|
ichain(i1, i2); // 1 2 3 4
|
||||||
|
ichainit(iarray([i1, i2])); // 1 2 3 4
|
||||||
|
icombine(iarray([0, 1]), iarray(["a", "b"])); // [0, "a"] [0, "b"] [1, "a"] [2, "b"]
|
||||||
|
izip(iarray([0, 1]), iarray(["a", "b"])); // [0, "a"] [1, "b"]
|
||||||
|
ialternate(iarray([0, 1]), iarray(["a", "b"])); // 0 "a" 1 "b"
|
||||||
|
|
||||||
|
// Type filter (result is a typed iterable)
|
||||||
|
ifiltertype(iarray([1, "a", 2, "b"]), (x): x is number => typeof x == "number"); // 1 2
|
||||||
|
class A {}
|
||||||
|
class B {}
|
||||||
|
ifilterclass(iarray([new A(), new B(), new A()]), A); // A A
|
||||||
|
```
|
27
mod.test.ts
27
mod.test.ts
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { describe, expect, it, mockfn } from "./deps.testing.ts";
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
it,
|
|
||||||
mockfn,
|
|
||||||
patch,
|
|
||||||
} from "https://code.thunderk.net/typescript/devtools/raw/1.3.0/testing.ts";
|
|
||||||
import {
|
import {
|
||||||
ialternate,
|
ialternate,
|
||||||
iarray,
|
iarray,
|
||||||
|
@ -20,6 +14,7 @@ import {
|
||||||
ifirst,
|
ifirst,
|
||||||
ifirstmap,
|
ifirstmap,
|
||||||
iforeach,
|
iforeach,
|
||||||
|
ilength,
|
||||||
iloop,
|
iloop,
|
||||||
imap,
|
imap,
|
||||||
imaterialize,
|
imaterialize,
|
||||||
|
@ -133,13 +128,23 @@ describe("Iterators", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("materializes an array from an iterator", () => {
|
it("materializes an array from an iterator", () => {
|
||||||
|
expect(imaterialize(IEMPTY)).toEqual([]);
|
||||||
expect(imaterialize(iarray([1, 2, 3]))).toEqual([1, 2, 3]);
|
expect(imaterialize(iarray([1, 2, 3]))).toEqual([1, 2, 3]);
|
||||||
|
expect(() => imaterialize(irepeat(null))).toThrow(
|
||||||
|
"Length limit on iterator materialize",
|
||||||
|
);
|
||||||
expect(() => imaterialize(iarray([1, 2, 3, 4, 5]), 2)).toThrow(
|
expect(() => imaterialize(iarray([1, 2, 3, 4, 5]), 2)).toThrow(
|
||||||
"Length limit on iterator materialize",
|
"Length limit on iterator materialize",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("counts items in an iterator", () => {
|
||||||
|
expect(ilength(IEMPTY)).toEqual(0);
|
||||||
|
expect(ilength(iarray([1, 2, 3]))).toEqual(3);
|
||||||
|
expect(ilength(irange())).toEqual(Infinity);
|
||||||
|
expect(ilength(iarray([1, 2, 3, 4, 5]), 3)).toEqual(Infinity);
|
||||||
|
});
|
||||||
|
|
||||||
it("creates an iterator in a range of integers", () => {
|
it("creates an iterator in a range of integers", () => {
|
||||||
checkit(irange(4), [0, 1, 2, 3]);
|
checkit(irange(4), [0, 1, 2, 3]);
|
||||||
checkit(irange(4, 1), [1, 2, 3, 4]);
|
checkit(irange(4, 1), [1, 2, 3, 4]);
|
||||||
|
@ -225,6 +230,8 @@ describe("Iterators", () => {
|
||||||
it("maps an iterator", () => {
|
it("maps an iterator", () => {
|
||||||
checkit(imap(IEMPTY, (i) => i * 2), []);
|
checkit(imap(IEMPTY, (i) => i * 2), []);
|
||||||
checkit(imap(irange(3), (i) => i * 2), [0, 2, 4]);
|
checkit(imap(irange(3), (i) => i * 2), [0, 2, 4]);
|
||||||
|
const r: Iterable<string> = imap(irange(3), (i) => i.toString());
|
||||||
|
checkit(r, ["0", "1", "2"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reduces an iterator", () => {
|
it("reduces an iterator", () => {
|
||||||
|
@ -239,7 +246,7 @@ describe("Iterators", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("filters an iterator with a type guard", () => {
|
it("filters an iterator with a type guard", () => {
|
||||||
let result = ifiltertype(
|
let result: Iterable<number> = ifiltertype(
|
||||||
<(number | string)[]> [1, "a", 2, "b"],
|
<(number | string)[]> [1, "a", 2, "b"],
|
||||||
(x): x is number => typeof x == "number",
|
(x): x is number => typeof x == "number",
|
||||||
);
|
);
|
||||||
|
@ -250,7 +257,7 @@ describe("Iterators", () => {
|
||||||
let o1 = new A();
|
let o1 = new A();
|
||||||
let o2 = new A();
|
let o2 = new A();
|
||||||
let o3 = new B();
|
let o3 = new B();
|
||||||
let result = ifilterclass([1, "a", o1, 2, o2, o3, "b"], A);
|
let result: Iterable<A> = ifilterclass([1, "a", o1, 2, o2, o3, "b"], A);
|
||||||
checkit(result, [o1, o2]);
|
checkit(result, [o1, o2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
25
mod.ts
25
mod.ts
|
@ -133,6 +133,25 @@ export function imaterialize<T>(iterable: Iterable<T>, limit = 1000000): T[] {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count items in an iterator
|
||||||
|
*
|
||||||
|
* To avoid counting infinite iterators (and bursting memory), the item count is limited by
|
||||||
|
* the *limit* parameter, and Infinity will be returned after that many.
|
||||||
|
*/
|
||||||
|
export function ilength<T>(iterable: Iterable<T>, limit = 1000000): number {
|
||||||
|
let result = 0;
|
||||||
|
|
||||||
|
for (let _ of iterable) {
|
||||||
|
if (result >= limit) {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
result += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate over natural integers
|
* Iterate over natural integers
|
||||||
*
|
*
|
||||||
|
@ -451,12 +470,12 @@ export function iunique<T>(
|
||||||
): Iterable<T> {
|
): Iterable<T> {
|
||||||
return {
|
return {
|
||||||
[Symbol.iterator]: function* () {
|
[Symbol.iterator]: function* () {
|
||||||
let done: T[] = [];
|
let done: Set<T> = new Set();
|
||||||
let n = limit;
|
let n = limit;
|
||||||
for (let value of iterable) {
|
for (let value of iterable) {
|
||||||
if (done.indexOf(value) < 0) {
|
if (!done.has(value)) {
|
||||||
if (n-- > 0) {
|
if (n-- > 0) {
|
||||||
done.push(value);
|
done.add(value);
|
||||||
yield value;
|
yield value;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unique count limit on iterator");
|
throw new Error("Unique count limit on iterator");
|
||||||
|
|
Loading…
Reference in a new issue