Package Exports
- node-enumerable
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (node-enumerable) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
node-enumerable
ES2017 ready LINQ library written in TypeScript.
Table of contents
- Requirements
- Installation
- Usage
- Playground / demos
- Examples
- Documentation
- License
- Tests
Requirements [↑]
- a ES2019 compatible environment like modern browsers or NodeJS
- TypeScript 4.1 or later (ONLY when using defintion files)
Installation [↑]
NodeJS [↑]
Run
npm install node-enumerable --saveinside project folder to install the module.
The module requires at least Node.js 12+.
Browser [↑]
Download the latest version from here.
<html>
<head>
<!-- node-enumerable -->
<script type="text/javascript" src="js/enumerable.js"></script>
</head>
<body>
<script type="text/javascript">
// test code
let seq = Enumerable.create(1, 2, 3);
for (let item of seq) {
alert(item);
}
</script>
</body>
</html>Usage [↑]
Create a sequence [↑]
const Enumerable = require('node-enumerable');
function *testGenerator() {
yield 111;
yield 222;
yield 333;
}
// from a list of values / objects with variable length
let seq1 = Enumerable.create(1, 'MK', true, null, {});
// from an array
let seq2 = Enumerable.from([11, 22, 33, 44]);
// from a generator
let seq3 = Enumerable.from( testGenerator() );
// from a string
//
// 'A', 'j', 'n', 'a', 't'
let seq4 = Enumerable.from('Ajnat'); // alt: Enumerable.fromString('Ajnat');
// range of numbers: 2, 3, 4, 5, 6
let seq5 = Enumerable.range(2, 5);
// 5979 'TM' strings
let seq6 = Enumerable.repeat('TM', 5979);
// build, using factory function
//
// 'item_1', 'item_2', 'item_3'
let seq7 = Enumerable.build((cancel, index) => {
if (index < 3) {
return 'item_' + (index + 1);
}
else {
cancel(); // we tell that we
// want to cancel here
}
});
// build, using factory function
// by building a flatten list
//
// 1, 10, 100, 2, 20, 200, 3, 30, 300
let seq8 = Enumerable.buildMany((cancel, index) => {
let n = index + 1;
return [ n, n * 10, n * 100 ];
}, 3); // create 3 elements
//
// the 'build()' function has
// a same argument
// create empty sequence
let seq9 = Enumerable.empty();Work with them [↑]
let seq = Enumerable.create(5979, 23979, null, '23979', 1781, 241279);
let newSeq = seq.where((x) => x !== null) // remove all elements that are (null)
.skip(1) // skip one element (5979)
.take(3) // take next remaining 3 elements (23979, 23979, 1781)
.distinct() // remove duplicates
.select((x) => "" + x) // convert to strings
.order(); // order by element ascending
// you also can use the
// 'each' and 'forEach' methods
// of the sequence to do the
// following job
for (let item of newSeq) {
// [0] 1781
// [1] 23979
console.log(item);
}Most methods are chainable as in .NET context.
Async operations [↑]
const FS = require('fs');
const Path = require('path');
let seq = Enumerable.create('file1.txt', 'file2.txt', 'file3.txt', 'file4.txt');
seq.async((context) => {
if (context.isFirst) {
context.result = 0; // initialize a counter
// value for the result
// s. 'counter' parameter
// of then() method below
}
// [0] file1.txt
// [1] file2.txt
// [2] file3.txt
// [3] file4.txt
let fileName = context.item;
let fullPath = Path.join(__dirname, fileName);
if (context.index < 2) {
FS.readFile(fullPath, (err, data) => {
if (err) {
context.reject(err); // has to be called if action
// FAILED with the error object
// or value as argument
}
else {
++context.result; // update counter value
// for the result
// s. 'counter' of then()
// method below
context.resolve(); // has to be invoked if
// invocation was SUCCESSFUL
}
});
}
else {
context.cancel(); // cancel at 3rd element
}
}).then((counter) => {
// OK
console.log('Number of loaded files: ' + counter); // 2
}).catch((err) => {
console.log('One action failed: ' + err);
});The context argument of the async() method uses the AsyncActionContext interface.
Playground / demos [↑]
You can test all features in your browser.
Examples [↑]
Filters [↑]
// distinct()
// 1, 2, 4, 3
Enumerable.create(1, 2, 4, 2, 3)
.distinct();
// distinctBy()
// "grape", "passionfruit", "banana", "raspberry"
Enumerable.create("grape", "passionfruit", "banana", "mango",
"orange", "raspberry", "apple", "blueberry")
.distinctBy(x => x.length);
// except()
// 2.0, 2.1, 2.3, 2.4, 2.5
Enumerable.create(2.0, 2.1, 2.2, 2.3, 2.4, 2.5)
.except([2.2]);
// intersect()
// 26, 30
Enumerable.create(44, 26, 92, 30, 71, 38)
.intersect([30, 59, 83, 47, 26, 4, 3]);
// ofType()
// '5979', 'Tanja'
Enumerable.create(1, '5979', 2, 'Tanja', 3)
.ofType('string'); // typeof x === 'string'
// union()
// 5, 3, 9, 7, 8, 6, 4, 1, 0
Enumerable.create(5, 3, 9, 7, 5, 9, 3, 7)
.union([8, 3, 6, 4, 4, 9, 1, 0]);
// where()
// 1, 2, 3
Enumerable.create(1, 2, 3, 4)
.where((x) => x < 4);Sort elements [↑]
// orderBy(), thenBy()
//
// "apple", "grape", "mango", "banana",
// "orange", "blueberry", "raspberry", "passionfruit"
Enumerable.create("grape", "passionfruit", "banana", "mango",
"orange", "raspberry", "apple", "blueberry")
.orderBy((x) => x.length) // complement: orderByDescending()
.thenBy((x) => x); // complement: thenByDescending()
// shorter: then()
// reverse()
// 4, 3, 2, 1
Enumerable.create(1, 2, 3, 4)
.reverse();
// rand()
// e.g.: 2, 5, 7, 8, 0, 4, 6, 9, 3, 1
Enumerable.range(0, 10)
.rand(); // alt: shuffle()Take / skip elements [↑]
// skip()
// 3, 4
Enumerable.create(0, 1, 2, 3, 4)
.skip(3);
// skipLast()
// 0, 1, 2, 3
Enumerable.create(0, 1, 2, 3, 4)
.skipLast();
// skipWhile()
// 55, 666, 77
Enumerable.create(22, 33, 44, 55, 666, 77)
.skipWhile((x) => x < 50);
// take()
// 0, 1, 2
Enumerable.create(0, 1, 2, 3, 4)
.take(3);
// takeWhile()
// 22, 33, 44
Enumerable.create(22, 33, 44, 55)
.takeWhile((x) => x < 50);Get one element [↑]
// elementAt()
// 33
Enumerable.create(11, 22, 33, 44)
.elementAt(2);
// elementAtOrDefault()
// 'TM'
Enumerable.create(11, 22, 33, 44)
.elementAtOrDefault(4, 'TM'); // out of range
// first()
// 11
Enumerable.create(11, 22, 33, 44)
.first();
// firstOrDefault()
// 'MK'
Enumerable.create()
.firstOrDefault('MK');
// last()
// 44
Enumerable.create(11, 22, 33, 44)
.last();
// lastOrDefault()
// 'PZ'
Enumerable.create()
.lastOrDefault('PZ');
// single()
// EXCEPTION, because we have more than one element
Enumerable.create(11, 22, 33, 44)
.single();
// singleOrDefault()
// 11
Enumerable.create(11)
.singleOrDefault('YS');All methods with NO OrDefault suffix will throw exceptions if no element was found.
You also can use a function as first argument for all of these methods that works as filter / condition:
// first()
// 22
Enumerable.create(11, 22, 33, 44)
.first((x) => x >= 20);Accumulators [↑]
// aggregate()
// " Marcel Joachim Kloubert"
Enumerable.create('Marcel', 'Joachim', 'Kloubert')
.aggregate((accumulator, item) => {
return accumulator += ' ' + item;
}, '');
// average()
// 2.5
Enumerable.create(1, 2, 3, 4)
.average();
// "M., Tanja"
Enumerable.create('M.', 'Tanja')
.joinToString(', ');Minimum / maximum values [↑]
// max()
// 3
Enumerable.create(1, 3, 2)
.max();
// min()
// 1
Enumerable.create(2, 3, 1, 2)
.min();Joins [↑]
class Person {
constructor(name: string) {
this.name = name;
}
public name: string;
}
class Pet {
constructor(name: string, owner: Person) {
this.name = name;
this.owner = owner;
}
public name: string;
public owner: Person;
}
let persons = [
new Person("Tanja"),
new Person("Marcel"),
new Person("Yvonne"),
new Person("Josefine")
];
let pets = [
new Pet("Gina", persons[1]),
new Pet("Schnuffi", persons[1]),
new Pet("Schnuffel", persons[2]),
new Pet("WauWau", persons[0]),
new Pet("Lulu", persons[3]),
new Pet("Asta", persons[1]),
];
// groupJoin()
//
// [0] 'Owner: Tanja; Pets: WauWau, Sparky'
// [1] 'Owner: Marcel; Pets: Gina, Schnuffi, Asta'
// [2] 'Owner: Yvonne; Pets: Schnuffel'
// [3] 'Owner: Josefine; Pets: Lulu'
Enumerable.from(persons)
.groupJoin(pets,
(person) => person.name,
(pet) => pet.owner.name,
(person, petsOfPerson) => {
let petList = petsOfPerson
.select(pet => pet.name)
.joinToString(', ');
return 'Owner: ' + person.name + '; Pets: ' + petList;
});
// join()
//
// [0] 'Owner: Tanja; Pet: WauWau'
// [1] 'Owner: Marcel; Pet: Gina'
// [2] 'Owner: Marcel; Pet: Schnuffi'
// [3] 'Owner: Marcel; Pet: Asta'
// [4] 'Owner: Yvonne; Pet: Schnuffel'
// [5] 'Owner: Josefine; Pet: Lulu'
Enumerable.from(persons)
.join(pets,
(person) => person.name,
(pet) => pet.owner.name,
(person, pet) => {
return 'Owner: ' + person.name + '; Pet: ' + pet.name;
});Groupings [↑]
// groupBy()
Enumerable.create("grape", "passionfruit", "blueberry",
"apple", "banana")
.groupBy(fruit => fruit[0].toLowerCase())
.each((grouping) => {
// grouping[0].key = 'g'
// grouping[0][0] = 'grape'
// grouping[1].key = 'p'
// grouping[1][0] = 'passionfruit'
// grouping[2].key = 'b'
// grouping[2][0] = 'blueberry'
// grouping[2][1] = 'banana'
// grouping[3].key = 'a'
// grouping[3][0] = 'apple'
});Projection [↑]
// flatten()
// 1, (false), 3, 44, '555', 66.6, (true)
Enumerable.from( [ [ 1, false, 3 ], 44, [ '555', 66.6, true ] ] )
.flatten();
// select()
// "MARCEL", "KLOUBERT"
Enumerable.create("Marcel", "Kloubert")
.select(x => x.toUpperCase());
// selectMany()
// 1, 10, 100, 2, 20, 200, 3, 30, 300
Enumerable.create(1, 2, 3)
.selectMany(x => [ x, x * 10, x * 100 ]);
// zip()
// "Marcel Kloubert", "Bill Gates", "Albert Einstein"
Enumerable.create('Marcel', 'Bill', 'Albert')
.zip(['Kloubert', 'Gates', 'Einstein', 'Adenauer'],
(firstName, lastName) => {
return `${firstName} ${lastName}`;
});Checks / conditions [↑]
// all()
// (false)
Enumerable.create(1, 2, '3', 4)
.all((x) => typeof x !== "string");
// any()
// (true)
Enumerable.create(1, 2, '3', 4)
.any((x) => typeof x === "string");
// contains()
// (true)
Enumerable.create(1, 2, '3')
.contains(3);
// not()
// 1, 2, 4
Enumerable.create(1, 2, '3', 4)
.not((x) => typeof x === "string");
// sequenceEqual()
// (false)
Enumerable.create(1, 2, 3)
.sequenceEqual([1, 3, 2]);Conversions [↑]
// toArray()
let jsArray = Enumerable.create(1, 2, 3, 4)
.toArray();
// toObject()
let obj = Enumerable.create(1, 2, 3, 4)
.toObject((item, index) => "item" + index);
// toLookup()
//
// lookup['A'][0] = 'Albert'
// lookup['B'][0] = 'Bill'
// lookup['B'][1] = 'barney'
// lookup['K'][0] = 'Konrad'
// lookup['M'][0] = 'Marcel'
let lookup = Enumerable.create('Bill', 'Marcel', 'barney', 'Albert', 'Konrad')
.toLookup(x => x[0].toUpperCase());Count [↑]
// 3
Enumerable.create(0, 1, 2)
.count(); // a second call will return 0
// if reset() method is not called
// 2
Enumerable.create(0, 1, 2)
.count((x) => x > 0);
// 4
Enumerable.create(11, 22, 33, 44)
.length(); // a second call will return
// the same value, because we have an array
// based sequence here
//
// a generator based sequence will behave as count()
// (false)
Enumerable.create(111, 222, 333)
.isEmpty();
// all are (false)
Enumerable.isNullOrEmpty(
Enumerable.create(1111, 2222, 3333)
);
Enumerable.isUndefinedNullOrEmpty(
Enumerable.create(11111, 22222, 33333)
);
Enumerable.isUndefinedNullOrEmpty(
Enumerable.create(0, true, false)
);Math [↑]
// abs()
// 1, 22.57, 444, NaN, -333.85, NaN
Enumerable.create(-1, 22.57, 444, true, -333.85, false)
.abs();
// ceil()
// -1, 23, 444, NaN, -333, NaN
Enumerable.create(-1, 22.47, 444, null, -333.85, false)
.ceil();
// cos()
// 0.004, -0.99996, -0.01
Enumerable.create(11, 22, 33)
.cos(); // complement: arcCos()
// cosH()
// 29937.07, 1792456423.07, 107321789892958.03
Enumerable.create(11, 22, 33)
.cosH(); // complement: arcCosH()
// exp()
// 2.72, 7.39, 20.09
Enumerable.create(1, 2, 3)
.exp();
// floor()
// -1, 23, 444, NaN, -334, NaN
Enumerable.create(-1, 22.47, 444.0, undefined, -333.85, true)
.floor();
// log()
// 0, 1, 2, 3, 4
Enumerable.create(1, 2, 4, 8, 16)
.log(2);
// pow()
// 1, 4, 9, 16
Enumerable.create(1, 2, 3, 4)
.pow(2);
// product()
// 24
Enumerable.create(1, 2, 3, 4)
.product();
// root()
// 1, 2, 3, 4
Enumerable.create(1, 8, 27, 64)
.root(3);
// round()
// -1, 23, 444, NaN, -334, 2, NaN
Enumerable.create(-1, 22.47, 444.0, undefined, -333.85, 1.5, true)
.round();
// sin()
// 0.84, 0.91, 0.14
Enumerable.create(1, 2, 3)
.sin(); // complement: arcSin()
// sinH()
// 1.18, 3.63, 10.02
Enumerable.create(1, 2, 3)
.sinH(); // complement: arcSinH()
// sqrt()
// 1, 2, 3, 4
Enumerable.create(1, 4, 9, 16)
.sqrt();
// sum()
// 10
Enumerable.create(1, 2, 3, 4)
.sum();
// tan()
// 1.72, -1.76, -0.01
Enumerable.create(111, 222, 333)
.tan(); // complement: arcTan()
// tanH()
// 0, 0.46, -0.76
Enumerable.create(0, 0.5, -1)
.tanH(); // complement: arcTanH()More [↑]
assert [↑]
let seq1 = Enumerable.range(0, 10);
seq1.assert((x) => {
return x % 2 !== 1;
}); // will throw an exception
// at second element (1)
let seq2 = Enumerable.range(0, 10);
seq2.assertAll((x) => {
return x % 2 !== 1;
}); // will throw an aggregated exception
// at the end
// for all odd valueschunk [↑]
let seq = Enumerable.range(0, 10);
for (let chunk of seq.chunk(3)) {
// [0] => [0, 1, 2]
// [1] => [3, 4, 5]
// [2] => [6, 7, 8]
// [3] => [9]
}clone [↑]
let father = Enumerable.create(0, 1, 2);
// create 3 clones of 'father'
for (let child of father.clone(3)) {
//TODO
}
// alt: father.clone().take(3)concat / concatArray [↑]
// 0, 1, 2, 'PZ', 'TM', 'MK'
Enumerable.create(0, 1, 2)
.concat(['PZ'], ['TM', 'MK']); // alt: append()
// 0, 111, 222, 'pz', 'tm', 'mk'
Enumerable.create(0, 111, 222)
.concatArray([ [ 'pz', 'tm' ], [ 'mk' ] ]); // alt: appendArray()consume [↑]
function createIteratorAndStorage(size) {
let storage = [];
return {
iterator: makeIterator(size, storage),
storage: storage,
};
}
function *makeIterator(size, storage) {
for (let i = 0; i < size; i++) {
yield i;
storage.push(i);
}
}
const OBJ = createIteratorAndStorage(100);
const SEQ = Enumerable.from(OBJ.iterator);
SEQ.consume(); // enumerates the 'iterator' in OBJ
// and fills the 'storage' in OBJdefaultIfEmpty / defaultArrayIfEmpty [↑]
// 0, 1, 2
Enumerable.create(0, 1, 2)
.defaultIfEmpty('PZ', 'TM', 'MK');
// 'PZ', 'TM', 'MK'
Enumerable.empty()
.defaultIfEmpty('PZ', 'TM', 'MK');
// 0, 11, 22
Enumerable.create(0, 11, 22)
.defaultArrayIfEmpty(['pz', 'tm', 'mk']);
// alt: defaultSequenceIfEmpty()
// 'pz', 'tm', 'mk'
Enumerable.empty()
.defaultArrayIfEmpty(['pz', 'tm', 'mk']);forAll [↑]
let arr = [];
try {
// alt: eachAll()
Enumerable.range(0, 5).forAll(x => {
if (x % 2 === 0) {
throw 'Error in value ' + x;
}
arr.push(x);
});
}
catch (e) {
// access the list of errors by
// 'e.errors'
// e.errors[0] = 'Error in value 0';
// e.errors[1] = 'Error in value 2';
// e.errors[2] = 'Error in value 3';
}
// arr[0] === 1
// arr[1] === 3
// arr[2] === 5intersperse / intersperseArray [↑]
// 0, '-', 1, '-', 2
Enumerable.range(0, 3)
.intersperse('-');
// -- or --
Enumerable.range(0, 3)
.intersperseArray( ['-'] );pipe [↑]
let arr1 = [];
let arr2 = [];
let seq = Enumerable.create(1, 2, 3).pipe((x) => {
arr1.push(x * 10);
});
for (let item of seq) {
arr2.push(item);
}
// arr1 = [10, 20, 30]
// arr2 = [1, 2, 3]popFrom / shiftFrom [↑]
let arr1 = [ 11, 22, 33 ];
for (let item of Enumerable.popFrom(arr1)) {
// [0] 33
// [1] 22
// [2] 11
}
// arr1 is empty now
let arr2 = [ 111, 222, 333 ];
for (let item of Enumerable.shiftFrom(arr2)) {
// [0] 111
// [1] 222
// [2] 333
}
// arr2 is empty nowprepend / prependArray [↑]
// 'PZ', 'TM', 'MK', 0, 1, 2
Enumerable.create(0, 1, 2)
.prepend(['PZ'], ['TM', 'MK']);
// 'pz', 'tm', 'mk', 0, 111, 222
Enumerable.create(0, 111, 222)
.prependArray([ [ 'pz', 'tm' ], [ 'mk' ] ]);pushTo [↑]
let arr = [];
Enumerable.create(0, 1, 2)
.pushTo(arr);
// arr: [0, 1, 2]random [↑]
for (let value of Enumerable.random(10)) {
// 10 random numbers
// between 0 and 1
}
for (let value of Enumerable.random(23979,
v => v * 5979)) {
// 23979 random numbers
// between 0 and 5979
}reset [↑]
let seq = Enumerable.create(0, 1, 2);
seq.each(x => {
console.log(x);
});
seq.reset()
.each(x => {
console.log(x * 2);
});trace [↑]
// write items via 'console.trace()'
Enumerable.create(0, 1, 2)
.trace();
// with formatter
Enumerable.create(1.2, 2.3, 3.45)
.trace(x => 'Item: ' + x);Documentation [↑]
The API documentation can be found here.
License [↑]
Tests [↑]
Go to the module folder and run
tsc
npm testto start unit tests from test/ subfolder.
