iterablejs v0.5.0
iterablejs
A functional, lazy-loading data manipulation library for Javascript.
let result = new Iterable([1, 2, 3, 4, 5, 4, 3, 2, 1])
.distinct() // 1, 2, 3, 4, 5
.select(x => x*2) // 2, 4, 6, 8, 10
.reverse() // 10, 8, 6, 4, 2
.skip(1) // 8, 6, 4, 2
.take(2) // 8, 6
.average(); // 7Installation
npm install iterablejsUsage
An Iterable can wrap any item which defines Symbol.iterator, including an Array, Map, Set, or Generators, and can also wrap GeneratorFunctions which do not require any arguments.
import Iterable from 'iterablejs';
let iter1 = new Iterable([1, 2, 3]),
iter2 = new Iterable(new Set([1, 2, 3, 1, 2])),
iter3 = new Iterable(new Map([['a', 1], ['b', 2]])),
iter4 = new Iterable(function*() { yield 1; yield 2; yield 3 }),
iter5 = new Iterable({ *[Symbol.iterator]() { yield 1; yield 2; yield 3; }});For convenience, iterablejs also exposes a method called iter to assist with constructing Iterable objects.
import { iter } from 'iterablejs';
let iterable = iter([1, 2, 3, 4, 5]);Default task
- Install node.js
- Clone the iterablejs project
- Run
npm install - Run
gulp- Executes tests
- Cleans dist
- Lints source
- Builds source
- Watches source and tests
API
Links
- Iterable
- #aggregate()
- #any()
- #at()
- #average()
- #concat()
- #contains()
- #distinct()
- #empty()
- #every()
- #filter()
- #first()
- #firstOrDefault()
- #flatten()
- #full()
- #group()
- #groupBy()
- #intersect()
- .iter()
- #join()
- #last()
- #lastOrDefault()
- #length()
- #map()
- #max()
- #merge()
- #min()
- #orderBy()
- #orderByDescending()
- #reduce()
- #reverse()
- #select()
- #skip()
- #sum()
- #take()
- #takeWhile()
- #toArray()
- #union()
- #unionAll()
- #unwind()
- #where()
- #while()
- #zip()
- MultiIterable
- OrderedIterable
Iterable
#aggregate()
aggregate(func: Function): Any
aggregate(func: Function, seed: Any): AnyReduces an Iterable to a single value, using a callback and an optional seed value.
let val = iter([2, 3, 4, 5])
.aggregate((carry, current) => {
return carry + current;
}, 1);
//=> 15If no seed value is provided, then the first yielded element will be used as the seed.
let val = iter([1, 2, 3, 4, 5])
.aggregate((carry, current) => {
return carry + current;
});
//=> 15#any()
any(predicate: Function): BooleanReturns a boolean, indicating whether at least one of the values in the Iterable passes a given predicate.
let passed = iter([1, 2, 3, 4, 5]).any(x => x > 3);
//=> true
let failed = iter([1, 2, 3, 4, 5]).any(x => x < 1);
//=> falseIf no predicate is provided, then a check for null and undefined will be used.
let passed = iter([null, undefined, 0]).any();
//=> true
let failed = iter([null, undefined, null]).any();
//=> false#at()
at(index: Number): AnyReturns the value at the provided zero-based index of yielded elements.
let val = iter([1, 2, 3, 4, 5]).at(3);
//=> 4If the index provided is beyond the length of the Iterable, then undefined will be returned.
let val = iter([1, 2, 3, 4, 5]).at(6);
//=> undefined#average()
average(): Number
average(selector: Function): NumberReturns the average of any numbers yielded by the Iterable.
let val = iter([1, 2, 3, 4, 5]).average();
//=> 3If a selector is provided, it will be used to select which elements are yielded.
let val = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).average(x => x.a);
//=> 3If the Iterable is empty, then NaN will be returned.
let val = iter([]).average();
//=> NaN#concat()
concat(...args): IterableReturns an Iterable which will iterate over the original iterable followed by all iterable arguments in sequence.
let iterable = iter([1, 2]).concat(new Set([3, 4]), function*() { yield 5; yield 6; });
//=> 1, 2, 3, 4, 5, 6#contains()
contains(item: Any): BooleanReturns a boolean indicating whether or not the Iterable contains a given element.
let passed = iter([1, 2, 3, 4, 5]).contains(5);
//=> true
let failed = iter([1, 2, 3, 4, 5]).contains(0);
//=> falseNote that contains will use a deep equals method for comparison.
let passed = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).contains({a: 3, b: 4});
//=> true#distinct()
distinct(): Iterable
distinct(hasher: Function): IterableReturns an Iterable which will yield only distinct elements.
let iterable = iter([1, 2, 3, 4, 5, 4, 3, 2, 1]).distinct();
//=> 1, 2, 3, 4, 5Note that distinct uses a Set to determine if elements are unique. If a hasher is provided, it will be used to hash each element before adding it to the Set.
let iterable = iter([{a: 1}, {a: 1}]).distinct();
//=> {a: 1}, {a, 1}
let iterable = iter([{a: 1}, {a: 1}]).distinct(x => x.a);
//=> {a: 1}#empty()
empty(): BooleanReturns a boolean indicating whether or not every element yielded from the Iterable is considered empty.
let passed = iter([
null,
undefined,
false,
0,
'',
[],
new Set(),
new Map()
]).empty();
//=> true
let failed = iter([0, null, undefined, 4, '', new Set(), new Map()]).empty();
//=> false#every()
every(): Boolean
every(predicate: Function): BooleanReturns a boolean indicating whether or not every element yielded from the Iterable passes a given predicate.
let passed = iter([1, 2, 3, 4, 5]).every(x => typeof x === 'number');
//=> true
let failed = iter([1, 2, '3', 4, 5]).every(x => typeof x === 'number');
//=> falseIf no predicate is provided, then a check for null and undefined will be used.
let failed = iter([1, 2, 3, null, 5]).every();
//=> false#filter()
filter(): Iterable
filter(predicate: Function): IterableAn alias for where.
#first()
first(): Any
first(predicate: Function): AnyReturns the first non-null, non-undefined element yielded from the Iterable.
let val = iter([1, 2, 3, 4, 5]).first();
//=> 1let val = iter([null, 2, 3, 4, 5]).first();
//=> 2If a predicate is provided, then the first element yielded from the Iterable which passes the predicate will be returned.
let val = iter([1, 2, 3, 4, 5]).first(x => x > 2);
//=> 3If the Iterable does not yield any elements or if no elements pass a given predicate, then undefined will be returned.
let val = iter([1, 2, 3, 4, 5]).first(x => x > 5);
//=> undefined#firstOrDefault()
firstOrDefault(default: Any): Any
firstOrDefault(predicate: Function, default: Any): AnyReturns the first element yielded from the Iterable, or the default if no element is yielded.
let val = iter([]).firstOrDefault(1);
//=> 1If a predicate is provided, then the first element yielded from the Iterable which passes the predicate will be returned.
If no value passes the predicate, then the default will be returned.
let val = iter([1, 2, 3, 4, 5]).firstOrDefault(x => x > 5, 6);
//=> 6#flatten()
flatten(): IterableReturns an Iterable which yields a flattened list of elements.
let iterable = iter([1, 2, 3, [4, 5, 6], [7, [8, [9]]]]).flatten();
//=> 1, 2, 3, 4, 5, 6, 7, 8, 9#full()
full(): BooleanReturns a boolean indicating whether every element yielded from the Iterable is considered non-empty.
let passed = iter([1, 2, 3, 4, 5]).full();
//=> true
let failed = iter([1, 2, 3, '', 5]).full();
//=> false#group()
group(keySelector: Function): IterableReturns an Iterable which yields the iterable grouping of elements based on the provided keySelector.
let invoices = [
{ product: 1, price: 20, qty: 5, customer: 3 },
{ product: 1, price: 20, qty: 2, customer: 1 },
{ product: 1, price: 20, qty: 8, customer: 1 },
{ product: 2, price: 40, qty: 1, customer: 2 },
{ product: 3, price: 60, qty: 3, customer: 3 }
];
let grouped = iter(invoices)
.group(invoice => invoice.product)
.select(group => ({
product: group.key,
priceSum: group.sum(invoice => invoice.price * invoice.qty),
qtySum: group.sum(invoice => invoice.qty),
distinctCustomerCount: group.unique(invoice => invoice.customer).length(),
recordCount: group.length()
}))
.orderBy(processed => processed.product);
/*=> { product: 1, priceSum: 300, qtySum: 15, distinctCustomerCount: 2, recordCount: 3 },
{ product: 2, priceSum: 40, qtySum: 1, distinctCustomerCount: 1, recordCount: 1 },
{ product: 3, priceSum: 180, qtySum: 3, distinctCustomerCount: 1, recordCount: 1 } */#groupBy()
groupBy(keySelector: Function): IterableAn alias for group.
#intersect()
intersect(iter: Any): Iterable
intersect(iter: Any, selector: Function): IterableReturns an Iterable which yields the elements which exist in both Iterables.
let iterable = iter([1, 2, 3, 4, 5]).intersect([1, 3, 5]);
//=> 1, 3, 5If a selector is provided, then it will be used to determine the intersection.
let iterable = iter([{a: 1}, {a: 2}, {a: 3}]).intersect([{a: 1}, {a: 3}], x => x.a);
//=> {a: 1}, {a: 3}#join()
join(...args): MultiIterableReturns a MultiIterable which yields the cartesian product of all elements.
let iterable = iter([1, 2]).join([3, 4], [5, 6]);
/*=> [ 1, 3, 5 ],
[ 1, 3, 6 ],
[ 1, 4, 5 ],
[ 1, 4, 6 ],
[ 2, 3, 5 ],
[ 2, 3, 6 ],
[ 2, 4, 5 ],
[ 2, 4, 6 ] */A MultiIterable can also call #join() with additional iterable items at any point.
let iterable = iter([1, 2]).join([3, 4]);
/*=> [ 1, 3 ],
[ 1, 4 ],
[ 2, 3 ],
[ 2, 4 ] */
iterable = iterable.join([5, 6]);
/*=> [ 1, 3, 5 ],
[ 1, 3, 6 ],
[ 1, 4, 5 ],
[ 1, 4, 6 ],
[ 2, 3, 5 ],
[ 2, 3, 6 ],
[ 2, 4, 5 ],
[ 2, 4, 6 ] */#last()
last(): Any
last(predicate: Function): AnyReturns the last element yielded from the Iterable.
let val = iter([1, 2, 3, 4, 5]).last();
//=> 5If a predicate is provided, then the last element yielded from the Iterable which passes the predicate will be returned.
let val = iter([5, 4, 3, 2, 1]).last(x => x > 2);
//=> 3If the Iterable does not yield any elements or if no elements pass a given predicate, then undefined will be returned.
let val = iter([1, 2, 3, 4, 5]).last(x => x > 5);
//=> undefined#lastOrDefault()
lastOrDefault(default: Any): Any
lastOrDefault(predicate: Function, default: Any): AnyReturns the last element yielded from the Iterable, or the default if no element is yielded.
let val = iter([]).lastOrDefault(1);
//=> 1If a predicate is provided, then the last element yielded from the Iterable which passes the predicate will be returned.
let val = iter([1, 2, 3, 4, 5]).lastOrDefault(x => x < 3, 6);
//=> 2If no value passes the predicate, then the default will be returned.
let val = iter([1, 2, 3, 4, 5]).lastOrDefault(x => x > 5, 6);
//=> 6#length()
length(): NumberReturns the length of the Iterable.
let val = iter([1, 2, 3, 4, 5]).length();
//=> 5Note that if length is a property defined on the internal data and is a Number, then it will be used directly. If length is not available as a property, then the Iterable will be fully enumerated to determine length.
#map()
map(selector: Function): IterableAn alias for select.
#max()
max(): Number
max(selector: Function): NumberReturns the max of any numbers yielded by the Iterable.
let val = iter([1, 2, 3, 4, 5]).max();
//=> 5If a selector is provided, it will be used to select values for the comparison.
let val = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).max(x => x.b);
//=> 6#merge()
merge(iter: Any): IterableAn alias for zip.
#min()
min(): Number
min(selector: Function): NumberReturns the min of any numbers yielded by the Iterable.
let val = iter([1, 2, 3, 4, 5]).min();
//=> 1If a selector is provided, it will be used to select values for the comparison.
let val = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).min(x => x.b);
//=> 2#orderBy()
orderBy(selector: Function): OrderedIterable
orderBy(selector: Function, comparer: Function): OrderedIterable
orderBy(selector: Function, comparer: Function, descending: Boolean): OrderedIterableReturns an OrderedIterable which yields items in ascending order by using a stable quicksort.
let iterable = iter([
{a: 3, b: 4},
{a: 1, b: 2},
{a: 5, b: 6}
]).orderBy(x => x.a);
//=> {a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}If no comparer is provided, then the selected elements will be compared using < and >.
If a comparer is provided, it will be used instead.
let comparer = (x, y) => {
if (x > y)
return -1;
if (x < y)
return 1;
return 0;
};
let iterable = iter([
{a: 3, b: 4},
{a: 1, b: 2},
{a: 5, b: 6}
]).orderBy(x => x.a, comparer);
//=> {a: 5, b: 6}, {a: 3, b: 4}, {a: 1, b: 2}By default, orderBy will yield elements in an ascending order. If the OrderedIterable needs to yield elements in a reverse order, a boolean flag can be passed to flip the order.
let iterable = iter([
{a: 3, b: 4},
{a: 1, b: 2},
{a: 5, b: 6}
]).orderBy(x => x.a, undefined, true);
//=> {a: 5, b: 6}, {a: 3, b: 4}, {a: 1, b: 2}#orderByDescending()
orderByDescending(selector: Function): OrderedIterable
orderByDescending(selector: Function, comparer: Function): OrderedIterableReturns an OrderedIterable which yields elements in descending order.
let iterable = iter([
{a: 3, b: 4},
{a: 1, b: 2},
{a: 5, b: 6}
]).orderByDescending(x => x.a);
//=> {a: 5, b: 6}, {a: 3, b: 4}, {a: 1, b: 2}#reduce()
reduce(func: Function): Any
reduce(func: Function, seed: Any): AnyAn alias for aggregate.
#reverse()
reverse(): IterableReturns an Iterable which yields elements in a reverse order.
let iterable = iter([1, 2, 3, 4, 5]).reverse();
//=> 5, 4, 3, 2, 1#select()
select(selector: Function): IterableReturns an Iterable which yields mapped elements from the given selector.
let iterable = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).select(x => x.a);
//=> 1, 3, 5#skip()
skip(): Iterable
skip(count: Number): IterableReturns an Iterable which bypasses the given number of elements and then yields the remainder.
let iterable = iter([1, 2, 3, 4, 5]).skip(2);
//=> 3, 4, 5If the given number is greater than the number of elements, then no items are yielded.
let iterable = iter([1, 2, 3, 4, 5]).skip(6);
//=> (empty)If no number is provided, then the next element will not be yielded from the Iterable.
let iterable = iter([1, 2, 3, 4, 5]).skip();
//=> 2, 3, 4, 5#sum()
sum(): Number
sum(selector: Function): NumberReturns the sum of any numbers yielded by the Iterable.
let val = iter([1, 2, 3, 4, 5]).sum();
//=> 15If a selector is provided, it will be used to select values for the comparison.
let val = iter([
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6}
]).sum(x => x.b);
//=> 12#take()
take(): Iterable
take(count: Number): IterableReturns an Iterable which only yields the given number of elements.
let iterable = iter([1, 2, 3, 4, 5]).take(3);
//=> 1, 2, 3If the given number is greater than the number of elements, then all items are yielded.
let iterable = iter([1, 2, 3, 4, 5]).take(6);
//=> 1, 2, 3, 4, 5If no number is provided, then only the next element will be yielded from the Iterable.
let iterable = iter([1, 2, 3, 4, 5]).take();
//=> 1#takeWhile()
takeWhile(): Iterable
takeWhile(predicate: Function): IterableAn alias for while.
#toArray()
toArray(): ArrayReturns an Array containing every element yielded from the Iterable.
let arr = iter(function*() { yield 1; yield 2; yield 3; }).toArray();
//=> [ 1, 2, 3 ]#union()
union(...args): Iterable
union(...args, hasher: Function): IterableReturns an Iterable which yields a union of all given iterables.
let iterable1 = iter([ 1, 2, 3 ]);
let iterable2 = iter([ 3, 4, 5 ]);
let union = iterable1.union(iterable2);
//=> 1, 2, 3, 4, 5If hasher callback is provided, then it will be used to determine uniqueness of the elements.
let iterable1 = new Iterable([{ val: 1 }, { val: 2 }, { val: 3 }]);
let iterable2 = new Iterable([{ val: 3 }, { val: 4 }, { val: 5 }]);
let union = iterable1.union(iterable2, x => x.val);
/*=> { val: 1 },
{ val: 2 },
{ val: 3 },
{ val: 4 },
{ val: 5 } */Important: Due to the dynamic nature of the arguments, avoid passing an iterable Function or GeneratorFunction as the final argument to #union().
If you intend to use Function or GeneratorFunction as an iterable argument for #union(), consider either using #concat() followed by #distinct() instead, or wrapping each argument as an Iterable first.
#unionAll()
unionAll(...args): IterableAn alias for concat.
#unwind()
unwind(selector: Function): IterableUses the selector provided to pluck a sub-iterable out of a sequence of iterables, iterating over the selected piece and yielding a pair of the base and sub element for each of the sub elements.
let iterable = new Iterable([
{ id: 1, arr: [ 'a', 'b' ] },
{ id: 2, arr: [ 'c', 'd' ] },
{ id: 3, arr: [ 'e', 'f' ] },
{ id: 4, arr: [ 'g', 'h' ] },
{ id: 5, arr: [ 'i', 'j' ] }
])
.unwind(x => x.arr);
/*=> [ { id: 1, arr: [ 'a', 'b' ] }, 'a' ],
[ { id: 1, arr: [ 'a', 'b' ] }, 'b' ],
[ { id: 2, arr: [ 'c', 'd' ] }, 'c' ],
[ { id: 2, arr: [ 'c', 'd' ] }, 'd' ],
[ { id: 3, arr: [ 'e', 'f' ] }, 'e' ],
[ { id: 3, arr: [ 'e', 'f' ] }, 'f' ],
[ { id: 4, arr: [ 'g', 'h' ] }, 'g' ],
[ { id: 4, arr: [ 'g', 'h' ] }, 'h' ],
[ { id: 5, arr: [ 'i', 'j' ] }, 'i' ],
[ { id: 5, arr: [ 'i', 'j' ] }, 'j' ] */No mutation or mapping of the base element is assumed. Typically, this method would be used with a further aggregation like select() in order to map elements as in the following example:
let iterable = new Iterable([
{ id: 1, arr: [ 'a', 'b' ] },
{ id: 2, arr: [ 'c', 'd' ] },
{ id: 3, arr: [ 'e', 'f' ] },
{ id: 4, arr: [ 'g', 'h' ] },
{ id: 5, arr: [ 'i', 'j' ] }
])
.unwind(x => x.arr)
.select(([base, sub]) => ({
id: base.id,
letter: sub
}));
/*=> { id: 1, letter: 'a' },
{ id: 1, letter: 'b' },
{ id: 2, letter: 'c' },
{ id: 2, letter: 'd' },
{ id: 3, letter: 'e' },
{ id: 3, letter: 'f' },
{ id: 4, letter: 'g' },
{ id: 4, letter: 'h' },
{ id: 5, letter: 'i' },
{ id: 5, letter: 'j' } */#where()
where(): Iterable
where(predicate: Function): IterableReturns an Iterable which only yields elements which return true from the given predicate.
let iterable = iter([1, 2, 3, 4, 5]).where(x => x % 2 === 0);
//=> 2, 4If no predicate is provided, then null and undefined will not be yielded.
let iterable = iter(['', 2, 0, 4, null]).where();
//=> '', 2, 0, 4#while()
while(): Iterable
while(predicate: Function): IterableReturns an Iterable which yields elements until a predicate returns false.
let iterable = iter([1, 2, 3, 4, 5]).while(x => x < 4);
//=> 1, 2, 3If no predicate is provided, then a check for null and undefined will be used.
let iterable = iter([1, 2, 3, null, 5]).while();
//=> 1, 2, 3#zip()
zip(iter: Any, selector: Function): IterableReturns an Iterable which applies a function to both Iterables, yielding a sequence of the results.
let iterable = iter([1, 2, 3, 4, 5]).zip([1, 2, 3, 4, 5], (x, y) => x + y);
//=> 2, 4, 6, 8, 10.empty()
Iterable.empty()Returns an Iterable that does not yield any elements.
let iterable = Iterable.empty();
//=> (empty).iter()
iter(...args: any)Constructs an Iterable from the given arguments.
let iterable = Iterable.iter([1, 2, 3, 4, 5]);
//=> 1, 2, 3, 4, 5If no arguments are provided, an empty Iterable is returned.
let iterable = Iterable.iter();
//=> (empty)If more than one argument is provided, a MultiIterable is returned.
let iterable = Iterable.iter([1, 2], [3, 4]);
/*=> [1, 3],
[1, 4],
[2, 3],
[2, 4] */OrderedIterable
#thenBy()
thenBy(selector: Function): OrderedIterable
thenBy(selector: Function, comparer: Function): OrderedIterable
thenBy(selector: Function, comparer: Function, descending: Boolean): OrderedIterableReturns an OrderedIterable with a subsequent level of sorting.
let iterable = iter([
{ id: 1, num: 11, num2: 44, num3: 88 },
{ id: 2, num: 22, num2: 44, num3: 66 },
{ id: 3, num: 22, num2: 33, num3: 77 },
{ id: 4, num: 11, num2: 33, num3: 99 },
{ id: 5, num: 22, num2: 44, num3: 55 }
])
.orderBy(x => x.num)
.thenBy(x => x.num2);
/*=> { id: 4, num: 11, num2: 33, num3: 99 },
{ id: 1, num: 11, num2: 44, num3: 88 },
{ id: 3, num: 22, num2: 33, num3: 77 },
{ id: 2, num: 22, num2: 44, num3: 66 },
{ id: 5, num: 22, num2: 44, num3: 55 } */This method is only available on an OrderedIterable, and can make use of a custom comparer and descending flag in the same way as orderBy().
#thenByDescending()
thenByDescending(selector: Function): OrderedIterable
thenByDescending(selector: Function, comparer: Function): OrderedIterableReturns an OrderedIterable with a subsequent level of sorting in descending order.
let iterable = iter([
{ id: 1, num: 11, num2: 44, num3: 88 },
{ id: 2, num: 22, num2: 44, num3: 66 },
{ id: 3, num: 22, num2: 33, num3: 77 },
{ id: 4, num: 11, num2: 33, num3: 99 },
{ id: 5, num: 22, num2: 44, num3: 55 }
])
.orderBy(x => x.num)
.thenByDescending(x => x.num2);
/*=> { id: 1, num: 11, num2: 44, num3: 88 },
{ id: 4, num: 11, num2: 33, num3: 99 },
{ id: 2, num: 22, num2: 44, num3: 66 },
{ id: 5, num: 22, num2: 44, num3: 55 },
{ id: 3, num: 22, num2: 33, num3: 77 } */