node-enumerable
    TypeScript icon, indicating that this package has built-in type declarations

    6.0.0 • Public • Published

    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.

    Install

    npm i node-enumerable

    DownloadsWeekly Downloads

    231

    Version

    6.0.0

    License

    MIT

    Unpacked Size

    1.69 MB

    Total Files

    155

    Last publish

    Collaborators

    • avatar