npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

node-enumerable

v6.0.0

Published

ES2017 ready LINQ features written in TypeScript

Downloads

384

Readme

npm npm

node-enumerable

ES2017 ready LINQ library written in TypeScript.

Donate

Table of contents

  1. Requirements
  2. Installation
  3. Usage
  4. Playground / demos
  5. Examples
  6. Documentation
  7. License
  8. Tests

Requirements []

Installation []

NodeJS []

Run

npm install node-enumerable --save

inside 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 values

chunk []

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 OBJ

defaultIfEmpty / 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] === 5

intersperse / 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 now

prepend / 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 []

MIT license

Tests []

Go to the module folder and run

tsc
npm test

to start unit tests from test/ subfolder.