Add ilength and some documentation
This commit is contained in:
parent
f2016e38e2
commit
db54d4f444
8 changed files with 203 additions and 13 deletions
79
README.md
79
README.md
|
@ -1,3 +1,82 @@
|
|||
# typescript/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>
|
||||
```
|
3
doc/index
Normal file
3
doc/index
Normal file
|
@ -0,0 +1,3 @@
|
|||
about
|
||||
import
|
||||
use
|
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 {
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
mockfn,
|
||||
patch,
|
||||
} from "https://code.thunderk.net/typescript/devtools/raw/1.3.0/testing.ts";
|
||||
import { describe, expect, it, mockfn } from "./deps.testing.ts";
|
||||
import {
|
||||
ialternate,
|
||||
iarray,
|
||||
|
@ -20,6 +14,7 @@ import {
|
|||
ifirst,
|
||||
ifirstmap,
|
||||
iforeach,
|
||||
ilength,
|
||||
iloop,
|
||||
imap,
|
||||
imaterialize,
|
||||
|
@ -133,13 +128,23 @@ describe("Iterators", () => {
|
|||
});
|
||||
|
||||
it("materializes an array from an iterator", () => {
|
||||
expect(imaterialize(IEMPTY)).toEqual([]);
|
||||
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(
|
||||
"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", () => {
|
||||
checkit(irange(4), [0, 1, 2, 3]);
|
||||
checkit(irange(4, 1), [1, 2, 3, 4]);
|
||||
|
@ -225,6 +230,8 @@ describe("Iterators", () => {
|
|||
it("maps an iterator", () => {
|
||||
checkit(imap(IEMPTY, (i) => i * 2), []);
|
||||
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", () => {
|
||||
|
@ -239,7 +246,7 @@ describe("Iterators", () => {
|
|||
});
|
||||
|
||||
it("filters an iterator with a type guard", () => {
|
||||
let result = ifiltertype(
|
||||
let result: Iterable<number> = ifiltertype(
|
||||
<(number | string)[]> [1, "a", 2, "b"],
|
||||
(x): x is number => typeof x == "number",
|
||||
);
|
||||
|
@ -250,7 +257,7 @@ describe("Iterators", () => {
|
|||
let o1 = new A();
|
||||
let o2 = new A();
|
||||
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]);
|
||||
});
|
||||
|
||||
|
|
25
mod.ts
25
mod.ts
|
@ -133,6 +133,25 @@ export function imaterialize<T>(iterable: Iterable<T>, limit = 1000000): T[] {
|
|||
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
|
||||
*
|
||||
|
@ -451,12 +470,12 @@ export function iunique<T>(
|
|||
): Iterable<T> {
|
||||
return {
|
||||
[Symbol.iterator]: function* () {
|
||||
let done: T[] = [];
|
||||
let done: Set<T> = new Set();
|
||||
let n = limit;
|
||||
for (let value of iterable) {
|
||||
if (done.indexOf(value) < 0) {
|
||||
if (!done.has(value)) {
|
||||
if (n-- > 0) {
|
||||
done.push(value);
|
||||
done.add(value);
|
||||
yield value;
|
||||
} else {
|
||||
throw new Error("Unique count limit on iterator");
|
||||
|
|
Loading…
Reference in a new issue