thing-iterators v2.0.0
thi.ng/iterators
Overview
Collection of approx. 50 composable, iterator-centric data processing functions, largely implemented as ES6 iterators / generators, inspired by clojure.core API. Written in TypeScript.
New in 2.0.0
All functions are defined as sub-modules and exposed as default exports. This is an additional feature. The full library can still be imported as before.
Function sub-modules use Kebab case whereas function names are in Camel case.
Installation
npm i thing-iterators
// import entire library
let ti = require("thing-iterators");
// single function (ES5)
let map = require("thing-iterators/map").default;
// single function (ES6 / TS)
import map from "thing-iterators/map";
API
In alphabetical order:
butLast<T>(input: Iterable<T>) => IterableIterator<T>
Yields iterator of all but the last value of input.
[...ti.butLast([])]
// []
[...ti.butLast([1])]
// []
[...ti.butLast([1,2])]
// [ 1 ]
[...ti.butLast([1,2,3])]
// [ 1, 2 ]
[...ti.butLast("hello")]
// [ "h", "e", "l", "l" ]
[...ti.butLast(ti.range(10))]
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
cached<T>(input: Iterable<T>) => CachedIterableIterator<T>
Consumes and lazily caches values of a finite input and returns
new iterator with added reset()
method. Calling this method
produces a new iterator instance starting from the beginning of the cache.
Allows for separate states and sharing of cached results among arbitrary
number of iterators. Original input is only consumed when attempting to
read beyond current cache boundary.
var a = ti.cached(ti.range(10));
var b = a.reset();
a.next();
// { done: false, value: 0 } -> from original
b.next();
// { done: false, value: 0 } -> from cache
b.next();
// { done: false, value: 1 } -> from original
a.next();
// { done: false, value: 1 } -> from cache
var angles = ti.cached(ti.range(0, 360, 45));
[...angles]
// [ 0, 45, 90, 135, 180, 225, 270, 315 ]
ti.zip(
angles.reset(),
ti.map(
ti.juxt(Math.sin, Math.cos),
ti.map(
(x)=> x * 180 / Math.PI,
angles.reset()
)
)
);
// { "0": [ 0, 1 ],
// "45": [ 0.8060754911159176, -0.5918127259718502 ],
// "90": [ -0.9540914674728181, -0.2995153947555356 ],
// "135": [ 0.3232114532680857, 0.9463267704531728 ],
// "180": [ 0.5715301650260188, -0.8205810566088714 ],
// "225": [ -0.9996891031455815, 0.02493385353255689 ],
// "270": [ 0.6117273014893283, 0.7910687129526641 ],
// "315": [ 0.27563309945482667, -0.9612629164203338 ] }
concat<T>(...inputs: Iterable<T>[]) => IterableIterator<T>
Produces iterator yielding lazy concatenation of given iterables. For
practical purposes none but the last input should be infinite. Any null
or
undefined
input arguments are skipped in the output.
[... ti.concat([1, 2, 3], [10, 20, 30], [100, 200, 300])]
// [ 1, 2, 3, 10, 20, 30, 100, 200, 300 ]
[...ti.concat.apply(null, ["abc", null, [1, 2, 3]])]
// [ "a", "b", "c", 1, 2, 3 ]
constantly<T>(x: T) => (...args: any[]) => T
Helper function returning a new fn which takes any number of args and always
returns x
.
// define an iterable of unknown size
iter = ti.takeWhile(x => x < 0.98, ti.repeatedly(()=> Math.random()));
// use map & reduce to determine size of iterable:
// `constantly` as mapping fn maps all values to 1
// then reduce is used to sum
ti.reduce((a, b)=> a + b, 0, ti.map(ti.constantly(1), iter))
// 241 (varying)
// some complex data transformation
times10 = (flt, coll) => [...ti.map(x => x * 10, ti.filter(flt, coll))];
// some user selectable config
filterModes = {
odd: x => (x % 2) == 1,
even: x => (x % 2) == 0,
all: ti.constantly(true)
};
// use `constantly` as pluggable bypass filter predicate fn
times10(filterModes[???], [1, 2, 3]);
// [10, 30] (odd)
// [20] (even)
// [10, 20, 30] (all)
consume(input: Iterator<any>, n?: number) => void
Helper function. Consumes & discards items from iterator (possibly for side effects) and optionally only up to the given number of steps.
cycle<T>(input: Iterable<T>) => IterableIterator<T>
Produces iterator which lazily caches & infinitely repeats sequence of input.
Important: Input MUST be finite, use take
to truncate input or output if necessary.
[... ti.take(10, ti.cycle(ti.range(3)))]
// [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]
dedupe<T>(input: Iterable<T>) => IterableIterator<T>
Produces iterator which discards successive duplicate values from input.
Important: Uses ===
for equality checks.
[... ti.dedupe([1, 2, 2, 3, 4, 4, 4, 3])]
// [1, 2, 3, 4, 3]
dedupeWith<T>(equiv: (a:T, b: T) => boolean, input: Iterable<T>) => IterableIterator<T>
Like dedupe
, but uses given function equiv
to determine equivalence
of successive values.
var coll = [{ a: 1 }, { a: 1, b: 2 }, { a: 2, b: 2 }, { a: 2, b: 2 }, { a: 3 }];
var eq = (a, b) => a.a === b.a;
[...ti.dedupeWith(eq, coll)]
// [ { a: 1 }, { a: 2, b: 2 }, { a: 3 } ]
dense<T>(input: Iterable<T>) => IterableIterator<T>
Yields iterator of all non-null
and non-undefined
values of input (e.g. a
sparse array).
var a = []
a[10] = 1;
a[20] = 2;
[...ti.dense(a)]
// [1, 2]
drop<T>(n: number, input: Iterable<T>) => IterableIterator<T>
Consumes & discards up to n
items from input and returns remaining
(possibly exhausted) iterator.
[... ti.drop(5, ti.range(10))]
// [5, 6, 7, 8, 9]
[... ti.drop(5, ti.range(3))]
// []
[... ti.take(5, ti.drop(5, ti.range()))]
// [ 5, 6, 7, 8, 9 ]
dropNth<T>(n: number, input: Iterable<T>) => IterableIterator<T>
Produces iterator which drops every n
th item from input.
[... ti.dropNth(2, ti.range(10))]
// [0, 2, 4, 6, 8]
[... ti.dropNth(3, ti.range(10))]
// [ 0, 1, 3, 4, 6, 7, 9 ]
dropWhile<T>(pred: (x: T) => boolean, input: Iterable<T>) => IterableIterator<T>
Consumes input and calls pred
for each item.
Discards all items whilst pred
returns true
, then returns remaining
(possibly exhausted) iterator.
[... ti.dropWhile((x) => x < 5, ti.range(10))]
// [5, 6, 7, 8, 9]
every<T>(pred: (x: T) => boolean, input: Iterable<T>) => boolean
Consumes input and calls pred
for each item. When pred
returns not true
,
process stops and returns false
itself. When all items pass the predicate,
the function returns true
.
If input is empty/exhausted prior to execution, every
will return false
.
var nums = ti.iterator([2, 4, 6, 8, 10]);
ti.every((x) => (x % 2) === 0, nums);
// true, all passed
nums.next()
// { value: undefined, done: true }
nums = ti.iterator([2, 3, 4]);
ti.every((x) => (x % 2) === 0, nums);
// false, `every` stopped at `3`
nums.next()
// { value: 4, done: false }
ti.every((x) => true, [])
// false (empty input)
filter<T>(pred: (x: T) => boolean, input: Iterable<T>) => IterableIterator<T>
Consumes input and calls pred
for each item.
Yields iterator of items for which pred
returned true
.
var multOf3 = (x) => (x % 3) === 0;
[... ti.filter(multOf3, ti.range(10))];
// [ 0, 3, 6, 9 ]
flatten(input: Iterable<any>, flatObjects = true) => IterableIterator<any>
Produces iterator which recursively flattens input (using flattenWith
).
Important: Recursion only applies to iterable types (excluding strings) and
objects (enabled by default, using objectIterator
, see below).
[... ti.flatten([1, [2, 3], [4, [5, ["abc"]]]])]
// [ 1, 2, 3, 4, 5, "abc" ]
[...ti.flatten([{ a: 1 }, { a: 23, b: 42, c: [1, 2, 3] }])]
// ["a", 1, "a", 23, "b", 42, "c", 1, 2, 3]
// don't flatten objects
[...ti.flatten([{ a: 1 }, [1, 2, 3], { a: 23, b: 42, c: [1, 2, 3] }], false)]
// [ { a: 1 }, 1, 2, 3, { a: 23, b: 42, c: [ 1, 2, 3 ] } ]
flattenWith(tx: (x: any) => any, input: Iterable<any>) => IterableIterator<any>
Produces iterator which selectively & recursively flattens input. The first arg
tx
is a transformation fn called for each non-null/undefined
value taken
from the input. It's used to check and possibly obtain an iteratable value for
further flattening. The transformer must return undefined
if the value can't
or shouldn't be flattened. If a value is returned it MUST be iterable.
The default transformer used by flatten
is:
let defaultTx = x =>
(typeof x !== "string" && (maybeIterator(x) || maybeObjectIterator(x))) ||
undefined;
// transformer allowing any iterable and strings
// if `x` is a string, return its numeric charcode sequence for flattening
let tx = x => typeof x == "string" ? ti.map(x => x.charCodeAt(0), x) : ti.maybeIterator(x);
[...ti.flattenWith(tx, ["ROOT", undefined, ["CHILD_1", null, ["CHILD_2"]]])]
// [ 82, 79, 79, 84, undefined, 67, 72, 73, 76, 68, 95, 49, null, 67, 72, 73, 76, 68, 95, 50 ]
fnil(fn: (...args: any[]) => any, ...ctors: (() => any)[]) => (...args: any[]) => any
Takes a function fn
and up to 3 ctor
functions. Produces a new function
that calls fn
, replacing a null
or undefined
arg with the value obtained
by calling its related positional ctor
fn (e.g. the first ctor is used to
supply first arg, etc.).
The function fn
can take any number of arguments, however only the first 3
are being potentially patched, how many depends on the number of ctor
fns
supplied.
hello = (greet, name) => `${greet}, ${name}!`;
helloEN = ti.fnil(hello, () => "Hi", () => "user");
helloDE = ti.fnil(hello, () => "Hallo", () => `Teilnehmer #${(Math.random()*100)|0}`);
helloEN(); // "Hi, user!"
helleEN(null,"toxi"); // "Hi, toxi!"
helloEN("Howdy","toxi"); // "Howdy, toxi!"
helloDE(); // "Hallo, Teilnehmer #42!"
inc = x => x + 1
// build new fn which calls ctor to supply 0 if arg is null or undefined
adder = ti.fnil(inc, () => 0);
adder();
// 1 => returns 0 (from ctor fn) + 1
adder(null);
// 1 => same as above
adder(10);
// 11 => arg was given, so 10 + 1
// generic object prop updater using given fn `f`
updateWith = f => (obj, id) => { return obj[id] = f(obj[id]), obj; }
// accumulate letter frequencies of string into object
// fnil is used here to avoid `NaN` each time an yet unknown letter is encountered
ti.reduce(updateWith(adder), {}, "abracadabra");
// { a: 5, b: 2, r: 2, c: 1, d: 1 }
frequencies(input: Iterable<any>) => IterableIterator<FrequencyPair[]>
Consumes input and yields iterator of 2-element tuples,
each [orig-item, freq]
(where freq
is the number of times
the item occurred).
Important: The input MUST be finite.
Implementation uses JSON.stringify
to determine equality.
[... ti.frequencies([[1,2], [2,3], [1,2], [2,4]])]
// [ [[1, 2], 2],
// [[2, 3], 1],
// [[2, 4], 1] ]
// letter frequencies
var isLetter = (x) => /[a-z]/i.test(x);
[... ti.frequencies(ti.filter(isLetter, "hello world!"))].sort((a, b) => b[1] - a[1])
// [ ["l", 3], ["o", 2], ["h", 1], ["e", 1], ["w", 1], ["r", 1], ["d", 1] ]
identity<T>(x: T) => T
Helper function. Simply returns its argument.
ti.identity() // undefined
ti.identity(null) // null
ti.identity(42) // 42
tests = [true, true, undefined, true]
// all tests succeeded?
ti.every(ti.identity, tests);
// false
// mark missing test results as success
ti.every(ti.fnil(ti.identity, () => true), tests);
// true
indexed<T>(input: Iterable<T>) => IterableIterator<[number, T]>
Yields iterator producing [index, value]
pairs of input.
[...ti.indexed("hello")]
// [ [ 0, "h" ], [ 1, "e" ], [ 2, "l" ], [ 3, "l" ], [ 4, "o" ] ]
interleave(...inputs: Iterable<any>[]) => IterableIterator<any>
Takes an arbitrary number of iterators and yields iterator of interleaved items from each input. Terminates as soon as one of the inputs is exhausted.
[... ti.interleave(ti.range(), ti.range(100, 200), ti.range(200, 205))]
// [ 0, 100, 200, 1, 101, 201, 2, 102, 202, 3, 103, 203, 4, 104, 204 ]
interpose(x: any, input: Iterable<any>) => IterableIterator<any>
Produces an iterator which injects x
inbetween each item from input iter
.
[... ti.interpose("/", ti.range(5))]
// [ 0, "/", 1, "/", 2, "/", 3, "/", 4 ]
iterate<T>(fn: (x: T) => T, seed: T) => IterableIterator<T>
Produces an iterator of the infinite results of iteratively applying fn
to seed
.
Important: Use take
to truncate sequence.
[... ti.take(10, ti.iterate((x) => x * 2, 1))]
// [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 ]
iterator<T>(x: Iterable<T>) => Iterator<T>
Syntax sugar. Returns x[Symbol.iterator]()
.
Most functions in this module call this automatically for each input.
juxt<T>(...fns: ((x: T) => any)[]) => (x: T) => any[]
Takes arbitrary number of functions and returns new function,
which takes single argument x
and produces array of result
values of applying each input function to x
(juxtoposition of the given transformation functions).
var kernel = ti.juxt(
(x) => x - 1,
(x) => x,
(x) => x + 1
);
kernel(1)
// [0, 1, 2]
[... ti.map(kernel, ti.range(3))]
// [ [-1, 0, 1], [0, 1, 2], [1, 2, 3] ]
last<T>(input: Iterable<T>) => T
Consumes a finite iterator and returns last item.
Important: Never attempt to use with an infinite input!
ti.last(ti.range(10))
// 9
// last item of truncated infinite input
ti.last(ti.take(10, ti.range()))
// 9
map<T>(fn: (...args: any[]) => T, ...inputs: Iterable<any>[]) => IterableIterator<T>
Consumes an arbitrary number of iterators and applies fn
to each of their values.
Produces iterator of results. Iteration stops as soon as one of the inputs is exhausted.
The mapping fn
should accept as many arguments as there are inputs to map
.
Provides a fast path for single input mapping.
[... ti.map((x)=> x*10, ti.range(10))]
// [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
[... ti.map((x, y, z) => [x, y, z], ti.range(5), ti.range(0, 100, 10), ti.range(0, 1000, 100))]
// [ [0, 0, 0], [1, 10, 100], [2, 20, 200], [3, 30, 300], [4, 40, 400] ]
mapcat<T>(fn: (...args: any[]) => Iterable<T>, ...inputs: Iterable<any>[]) => IterableIterator<T>
Like map
, but expects mapping fn to return an iterable result and
produces iterator which yields the flat concatenation of results
(only the first level of nesting is removed).
null
or undefined
values returned by the mapping fn are skipped
in the output.
[... ti.mapcat((x) => ti.repeat(x, 3), "hello")]
// [ "h", "h", "h", "e", "e", "e", "l", "l", "l", "l", "l", "l", "o", "o", "o" ]
[...ti.mapcat((x) => x < 5 ? ti.repeat(x,x) : null, ti.range(10))]
// [ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 ]
mapIndexed<T>(fn: (i: number, ...args: any[]) => T[], ...inputs: Iterable<any>[]) => IterableIterator<T>
Like map
, but too passes a monotonically increasing index
as first
argument to mapping fn.
[... ti.mapIndexed((i, a, b) => [i, a, b], "hello", "there")]
// [ [0, "h", "t"],
// [1, "e", "h"],
// [2, "l", "e"],
// [3, "l", "r"],
// [4, "o", "e"] ]
maybeIterator(x: any) => any
Helper function, returning arg's iterator (if present) or else undefined
.
maybeObjectIterator(x: any) => any
Helper function, checks if x
is object-like (but no generator) and returns
objectIterator(x)
or else undefined
.
objectIterator(x: any) => IterableIterator<any[]>
Produces iterator of an object"s key/value pairs.
[... ti.objectIterator({a: 23, b: 42, c: [1, 2, 3]})]
// [ ["a", 23], ["b", 42], ["c", [1, 2, 3]] ]
partition<T>(n: number, step: number, input: Iterable<T>, all = false) => IterableIterator<T[]>
Produces iterator of fixed size partitions/chunks of input values.
Produces overlapping partitions, if step
< partition size n
.
Unless the optional all
flag is enabled, returns only completely filled
partitions. If all = true
, the last partition
produced may have less than n
items (though never empty).
[... ti.partition(3, 3, ti.range(10))]
// [ [0, 1, 2], [3, 4, 5], [6, 7, 8] ]
[... ti.partition(3, 3, ti.range(10), true)]
// [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [9] ]
[... ti.partition(3, 1, ti.range(10))]
// [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5],
// [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9] ]
[... ti.partition(3, 5, ti.range(10))]
// [ [0, 1, 2], [5, 6, 7] ]
partitionBy<T>(fn: (x: T) => any, input: Iterable<T>) => IterableIterator<T[]>
Produces iterator of partitions/chunks of input values. Applies fn
to each item
and starts new partition each time fn
returns new result.
[... ti.partitionBy((x) => x / 5 | 0, ti.range(11))]
// [ [0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10] ]
randomSample<T>(prob: number, input: Iterable<T>) => IterableIterator<T>
Produces iterator which consumes input and yields values with given probability (0 .. 1 range).
[... ti.randomSample(0.1, ti.range(100))]
// [ 10, 13, 16, 21, 22, 24, 32, 35, 37, 81, 93 ]
range(from?: number, to?: number, step?: number) => IterableIterator<number>
Produces iterator of monotonically increasing or decreasing values with optional step
value.
- If called without arguments, produces values from 0 .. +∞.
- If called with 1 arg: 0 ... n (exclusive)
- If called with 2 arg:
from
...to
(exclusive)
If from
> to
and no step
is given, a step
of -1
is used.
[... ti.take(5, ti.range())]
// [0, 1, 2, 3, 4]
[... ti.range(5)]
// [0, 1, 2, 3, 4]
[... ti.range(100, 105)]
// [100, 101, 102, 103, 104]
[... ti.range(5,0)]
// [5, 4, 3, 2, 1]
[... ti.range(0, 50, 10)]
// [0, 10, 20, 30, 40]
[... ti.range(50, -1, -10)]
// [50, 40, 30, 20, 10, 0]
reduce<A, B>(rfn: (acc: B, x: A) => B | ReducedValue<B>, acc: B, input: Iterable<A>) => B
Consumes and reduces input using reduction function rfn
,
then returns reduced result value. rfn
can abort reduction
process by returning a value wrapped using reduced(x)
.
If this is done, then this value is unwrapped and returned
as final result.
If input is empty, returns initial acc
umulator arg.
ti.reduce((acc, x) => acc + x, 0, ti.range(10))
// 45
// infinite input with early termination
ti.reduce((acc, x) => { return acc += x, acc >= 15 ? ti.reduced(acc) : acc }, 0, ti.range())
// 15
reductions<A, B>(rfn: (acc: B, x: A) => B | ReducedValue<B>, acc: B, input: Iterable<A>) => IterableIterator<B[]>
Like reduce
, but yields an iterator of all intermediate reduction results.
Always yields at least initial acc
umulator arg, even if input is empty.
Thus, the result is the equivalent of an exclusive scan operation
(with the exception of possible early bail out via reduced
).
[... ti.reductions((acc, x) => acc + x, 0, ti.range(10))]
// [ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 ]
// with early termination
[... ti.reductions((acc, x) => { return acc += x, acc >= 15 ? ti.reduced(acc) : acc }, 0, ti.range())]
// [ 0, 1, 3, 6, 10, 15 ]
reduced<T>(x: T) => ReducedValue<T>
For use inside reduction functions only. Wraps result in marker type to cause early termination of reduction (see example above).
repeat<T>(x: T, n?: number) => IterableIterator<T>
Produces an iterator of infinite (by default) repetitions of value x
.
If n
is given, produces only that many values.
[... ti.take(5, ti.repeat(42))]
// [42, 42, 42, 42, 42]
[... ti.repeat(42, 5)]
// [42, 42, 42, 42, 42]
repeatedly<T>(fn: () => T, n?: number) => IterableIterator<T>
Produces an iterator of infinite (by default) results of calling the no-arg fn
repeatedly.
If n
is given, produces only that many values.
[... ti.take(3, ti.repeatedly(() => Math.random()))]
// [ 0.9620186971807614, 0.8191901643942394, 0.5964328949163533 ]
[... ti.repeatedly(() => Math.random(), 3)]
// [ 0.46381477224416057, 0.22568030685532992, 0.5494769470662977 ]
reverse<T>(input: Iterable<T>) => IterableIterator<T>
Yields iterator lazily producing reverse result order of input (must be finite). If input is not already array-like (strings are for this purpose), the function first consumes & caches input as array.
[...ti.reverse([1, 2, 3])]
// [3, 2, 1]
[...ti.reverse("hello")]
// [ "o", "l", "l", "e", "h" ]
[...ti.reverse(ti.take(10, ti.iterate(x => x * 2, 1)))]
// [ 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 ]
some<T>(pred: (x: T) => boolean, input: Iterable<T>) => T
Consumes iterator and calls pred
for each item.
When pred
returns true
, process stops and returns this first successful item.
When none of the items pass the predicate, the function returns undefined
.
var nums = ti.iterator([1, 2, 3]);
ti.some((x) => (x % 2) === 0, nums);
// 2, the 1st value which passed
nums.next()
// { value: 3, done: false }
nums = ti.iterator([1, 2, 3]);
ti.some((x) => x > 3, nums);
// undefined
nums.next()
// { value: undefined, done: true }
take<T>(n: number, input: Iterable<T>) => IterableIterator<T>
Produces iterator of the first n
values of input
(or less than n
, if input is too short...)
[... ti.take(3, ti.range())]
// [ 0, 1, 2 ]
takeNth<T>(n: number, input: Iterable<T>) => IterableIterator<T>
Produces an iterator only yielding every n
th item from input.
[... ti.takeNth(3, ti.range(10))]
// [ 0, 3, 6, 9 ]
takeWhile<T>(pred: (x: T) => boolean, input: Iterable<T>) => IterableIterator<T>
Produces iterator which calls pred
for each item of input and terminates as
soon as pred
returns false
.
Important: Due to lack of look-ahead in the ES6 iterator API, the
value failing the given pred
will be lost when working with the original
iterator after takeWhile
.
var input = ti.range(10);
[... ti.takeWhile((x)=> x < 5, input)]
// [ 0, 1, 2, 3, 4 ]
[... input]
// note: `5` is missing (the value which failed takeWhile)
// [ 6, 7, 8, 9 ]
takeLast<T>(n: number, input: Iterable<T>) => IterableIterator<T>
Consumes input and produces iterator of the last n
values of input (or less
than n
, if input is too short...)
Important: Never attempt to use with infinite inputs!
[... ti.takeLast(5, ti.range(1000))]
// [ 995, 996, 997, 998, 999 ]
[... ti.takeLast(5, ti.range(3))]
// [ 0, 1, 2 ]
walk(fn: (x: any) => void, input: Iterable<any>, postOrder = false) => void
Recursively walks input and applies fn
to each element (for in-place editing
or side effects). Only iterable values and objects (but not strings) are
traversed further. Traversal is pre-order by default, but can be changed to
post-order via last arg.
// dummy SVG document
let doc = {
tag: "svg",
content: [
{
tag: "g",
content: [
{ tag: "rect" },
{ tag: "circle" }
]
},
{ tag: "circle", attr: { fill: "red" } }
]
};
// transformer fn for circle tags
// injects/overrides certain attribs
let circleTX = x => {
if (x.tag === "circle") {
x.attr = x.attr || {};
x.attr.x = Math.random()*100;
x.attr.y = Math.random()*100;
x.attr.r = 5;
}
};
// transform doc
ti.walk(circleTX, doc);
doc.content[0].content[1]
// { tag: "circle", attr: { x: 83.9269, y: 31.129, r: 5 } }
doc.content[1]
// { tag: "circle", attr: { fill: "red", x: 37.963, y: 87.521, r: 5 } }
walkIterator(input: Iterable<any>, postOrder = false) => IterableIterator<any>
Yields an iterator performing either pre-order (default) or post-order traversal of input. Only iterable values and objects (but not strings) are traversed further.
// pre-order traversal
[...ti.map(JSON.stringify, ti.walkIterator([[[1, [2]], [3, [4]]], [5]], false))]
// [ "[[[1,[2]],[3,[4]]],[5]]",
// "[[1,[2]],[3,[4]]]",
// "[1,[2]]",
// "1",
// "[2]",
// "2",
// "[3,[4]]",
// "3",
// "[4]",
// "4",
// "[5]",
// "5" ]
// post-order traversal
[...ti.map(JSON.stringify, ti.walkIterator([[[1, [2]], [3, [4]]], [5]], true))]
// [ "1",
// "2",
// "[2]",
// "[1,[2]]",
// "3",
// "4",
// "[4]",
// "[3,[4]]",
// "[[1,[2]],[3,[4]]]",
// "5",
// "[5]",
// "[[[1,[2]],[3,[4]]],[5]]" ]
zip(keys: Iterable<any>, vals: Iterable<any>, target?: any) => any
Takes an iterator of keys and iterator of values, pairwise combines items
from each input and associates them as key-value mappings in a given target
object (if target
is missing, returns new object).
Stops as soon as either input is exhausted.
ti.zip("abcdef", ti.range())
// { a: 0, b: 1, c: 2, d: 3, e: 4, f: 5 }
ti.zip(ti.range(5,10), ti.range(100,200), new Uint8Array(16))
// [ 0, 0, 0, 0, 0, 100, 101, 102, 103, 104, 0, 0, 0, 0, 0, 0 ]
var langs=[
{id: "js", name: "JavaScript"},
{id: "clj", name: "Clojure"},
{id: "ts", name: "TypeScript"}
];
ti.zip(ti.map((x)=> x.id, langs), langs)
// { js: { id: "js", name: "JavaScript" },
// clj: { id: "clj", name: "Clojure" },
// ts: { id: "ts", name: "TypeScript" } }
Building
Build requirements
Building & testing this project requires Typescript 2.1+ and Mocha globally installed:
npm i typescript mocha -g
cd thing-iterators
npm install
npm run build
Testing
npm link mocha
npm run test
Authors
- Karsten Schmidt
License
Apache Software License 2.0