iteratez
A powerful functional iterator, transformer, and mutator.
Out of the box you can iterate over arrays, objects, trees, sets, maps, linked-lists, iterables - and you can provide iteration capabilites to your own code no matter how complex the type (dynamically calculated, etc).
The iterator is lazy, so you can chain "views" and iteration is not done until you perform "operations" or "mutations" to the underlying source.
Features
- Array, object, tree, set, map, linked-list, and iterables out of the box.
- Iteration is lazy, so iteration is only done when it absolutely needs to be.
- Some operations can exit early and cease iteration saving time and resources.
- When iterating, you can stop at any time.
- If the underlying source supports it, remove a value.
- If the underlying source supports it, replace a value.
- You can chain views which don't cause iteration until an operation or mutation are called.
- You can call mutations to affect the underlying source.
- You can call operations to iterate and produce a result.
- You can create a reusable function to perform operations repeatedly.
- Create your own iterator.
You can see all of these features in the examples below.
Views
Returns an iterator...
where
: for a subset of the values.not
: for a subset of the values that don't pass test (opposite of where).transform
: that transforms the values to another type.reverse
: that iterates over the values in reverse order.exclude
: that excludes values found in another iterator.intersect
: that has common values in another iterator.sorted
: that is sorted based on some comparison.shuffle
: that is randomly ordered.unique
: that has only unique values.duplicates
: that has all the duplicate values.readonly
: that ignores mutations.keys
: only for the keys of the values (replace not supported).values
: only for the values (new key is index based).take
: that only iterates over the first X values.skip
: that skips the first X values.drop
: that drops off the last X values.append
: that is the original iterator + one or more iterators specified.prepend
: that is one or more iterators specified + the original iterator.gt
: that only has values greater than a value.gte
: that only has values greater than or equal to a value.lt
: that only has values less than a value.lte
: that only has values less than or equal to a value.fork
: that is this, but allows a function to perform fork operationssplit
: Splits the values into two iterators (pass/fail) based on a condition.unzip
: Splits the view into two iterates (keys/values).
Mutations
delete
: Removes values in the view from the source.overwrite
: Replaces values in the view from the source with a constant replacement.update
: Replace values in the view from the source with a dynamic replacement.extract
: Removes values in the view from the source and returns a new iterator with the removed values.
Operations
empty
: Determines if view contains zero values.has
: Determines if the view contains any values.contains
: Determines if the view contains a specific value.first
: Gets the first value in the view.last
: Gets the last value in the view.count
: Counts the number of values in the view.array
: Builds an array of the values in the view.set
: Builds a Set of the values in the view.object
: Builds an object of the values in the view.entries
: Builds an array of [key, value]
in the view.map
: Builds a Map of the values and keys in the view.group
: Builds an object of value arrays grouped by a value derived from each value.reduce
: Reduces the values in the view down to a single value.min
: Returns the minimum value in the view.max
: Returns the maximum value in the view.iterate
: Invokes a function for each value in the view.copy
: Copies the values in the view and returns a new iterator.changes
: Notifies you when values are added, removed, or still present on an iterator since the last time called.
Comparison Logic
The following chainable functions define how values should be compared.
numbers
: Set number comparison logic to the iterator.strings
: Set string comparison logic to the iterator.dates
: Set date comparison logic to the iterator.desc
: Reverses the comparison logic.withEquality
: Set a custom equality function.withComparator
: Set a custom comparison function.
Reset
The following function(s) allow you to change the source for iteration.
reset
: Sets a new source to iterate.
Other Functions
The following static functions exist to help iterate simple sources:
Iterate.array
: Iterates an array.Iterate.object
: Iterates the properties of an object, optionally just the properties explicitly set on the object.Iterate.tree
: Iterates trees.Iterate.linked
: Iterates linked-lists.Iterate.map
: Iterates MapsIterate.set
: Iterates SetsIterate.join
: Returns an iterator that iterates over one or more iterators.Iterate.zip
: Combines a key iterator and value iterator into one.Iterate.empty
: Iterates nothing.Iterate.entries
: Iterates an array of [key, value]
entries.Iterate.iterable
: Iterates any collection that implements iterable.Iterate.hasEntries
: Iterates any object which has the entries()
iterator.
Examples
The example is in Typescript, but iterator is available as iz.Iterate
and the function whic dynamically returns an iterator is iz.iterate
or simply iz
in JS
import iz, { Iterate } from 'iteratez';
let source = iz([1, 5, 7, 9, 10]);
let source = iz({
name: 'ClickerMonkey',
age: 30
});
let source = iz('string');
let source = iz(...source);
let source = iz([['key', 'value'], ['key', 'value']]);
let source = iz(new Map());
let source = iz(new Set());
let source = iz(ReadonlyArray | ReadonlyMap | ReadonlySet | Int8Array | ...)
let source = Iterate.tree( ... )(head);
let source = Iterate.linked( ... )(head);
let source = yourSource.yourIteratorGenerator();
source.each((value, key, iter) => {
if (someCondition(value)) {
iter.stop(42)
}
}).withResult((result) => {
});
source.each((value, key, iter) => {
if (someCondition(value)) {
iter.remove();
}
});
source.each((value, key, iter) => {
if (someCondition(value)) {
iter.replace(replacement);
}
});
let empty = source.empty();
let has = source.has();
let contains = source.contains(2);
let first = source.first();
let last = source.last();
let count = source.count();
let array = source.array();
let array = source.array(dest);
let set = source.set();
let object = source.object(value => value.id);
let object = source.object(value => value.id, dest);
let entries = source.entries():
let map = source.map();
let group = source.group(value => value.age);
let reduced = source.reduce(R, (T, R) => R);
let min = source.min();
let max = source.max();
let copy = source.copy():
let that = source.changes(onAdd, onRemove, onPresent);
source.delete();
source.where(x => x.id).delete();
source.extract();
source.overwrite(42);
source.where(x => x > 34).overwrite(12);
source.update(x => x * 2);
source.where(x => x.age > 0);
source.not(x => x.age > 0);
source.transform(x => x.name);
source.reverse();
source.exclude(anotherSource);
source.intersect(anotherSource);
source.sorted(comparator?);
source.shuffle(times?);
source.unique(equality?);
source.duplicates(onlyOnce?);
source.readonly();
source.keys();
source.values();
source.take(10);
source.skip(5);
source.drop(3);
source.append(anotherSource);
source.prepend(anotherSource);
source.gt(value, comparator?);
source.gte(value, comparator?);
source.lt(value, comparator?);
source.lte(value, comparator?);
source.fork(f => f.where(x => !!x.male).delete());
source.split(x => x.male);
source.split(x => x.male, (pass, fail) => {}):
source.unzip();
source.unzip((keys, values) => {});
source.withComparator((a, b) => number);
source.withEquality((a, b) => boolean);
source.numbers(ascending?, nullsFirst?);
source.strings(sensitive?, ascending?, nullsFirst?);
source.dates(equalityTimespan?, utc?, ascending?, nullsFirst?);
source.desc();
source.reset(['a', 'new', 'source', 'to', 'iterate']);
source.duplicates().has();
source.duplicates().delete();
source.where(x => x.age < 18).extract();
source.sorted().skip(5).take(10).array();
source.transform<string>(
value => value.name,
(replaceWith, current, value) => {
value.name = replaceWith;
}
).each((name, key, iter) => {
iter.replace(name.toUpperCase());
});
source.each((value, key, iter) => {
});
interface Node<T> {
value: T
next?: Node<T>
}
const linkedIterator = Iterate.linked<Node<string>, string>(
(node) => node.value,
(node) => node.next,
(node, prev) => prev.next = node.next,
(node, value) => node.value = value,
(node) => node.value.length
);
const head: Node<string> = ...;
linkedIterator(head)
.where(x => /regex/.test(x))
.fork(f => f.strings().sorted().take(5).delete())
.array();
interface Node<T> {
value: T
children?: Node<T>[]
}
const treeIterator = Iterate.tree<Node<string>, string>(
(node) => node.value,
(node) => node.children,
(node, value) => node.value = value
);
const head: Node<string> = ...
const depthFirstList = treeIterator(head).array();
const breadthFirstList = treeIterator(head, false).array();
Reusable Function
You can define a function which takes an iterator and performs any
number of operations. You can optionally have it return a result.
const fn = Iterate.func<string>(
source => source
.where(x => x.indexOf('a') !== -1)
.update(x => x.toUpperCase())
);
const a = ['apple', 'bit', 'cat'];
fn(a);
const fn = Iterate.func<string, number>(
(source, setResult) => source
.where(x => x.indexOf('a') !== -1)
.count(setResult)
.update(x => x.toUpperCase())
);
const a = ['apple', 'bit', 'cat'];
const b = fn(a);
const getPage = Iterate.func<Person, Person[], [number, number]>(
(source, setResult, offset, limit) => source
.skip(offset)
.take(limit)
.array(setResult)
);
const persons: Person[] = ...;
const page = getPage(persons, 5, 10);
Custom Iterators
You can add your own iterators to pick up your own types. If you are not using
TypeScript you can ignore and remove the types.
type DateRange = [Date, number];
function getDateIterator ([start, max]: DateRange)
{
return new Iterate<Date, number, DateRange>(iter =>
{
const curr = new Date(start.getTime());
for (let key = 0; key < max; key++)
{
const value = new Date(curr.getTime());
switch (iter.act(value, key))
{
case IterateAction.STOP:
return;
case IterateAction.REMOVE:
break;
case IterateAction.REPLACE:
break;
}
curr.setDate(curr.getDate() + 1);
}
});
}
import { Generators } from 'iteratez';
function isDateRange(s: any): s is DateRange {
return Array.isArray(s)
&& s.length === 2
&& s[0] instanceof Date
&& typeof s[1] === 'number'
;
}
Generators.unshift(s => isDateRange(s) ? getDateIterator(s) : false);
const dateIterator = iz([new Date(), 10]);
const dates = dateIterator.array();
import { Iterate } from 'iteratez';
declare module 'iteratez'
{
export function iterate (range: DateRange): Iterate<Date, number, DateRange>
}
import { iz } from 'iteratez';
const dateIterator = iz([new Date(), 3]);