JavaScript Iterator Class

后端 未结 7 1310
挽巷
挽巷 2020-12-29 07:55

Do you know a JavaScript library that implements a generic Iterator class for collections (be it Arrays or some abstract Enumerable) with a full set of features, like the Go

相关标签:
7条回答
  • 2020-12-29 08:02

    In the time since this question was asked JavaScript has added actual Iterators. Some built-in types, such as Array, Map, and String now have a default iteration behavior, but you can add your own to any object by including a next() function which returns one of two objects:

    {done:true}     /*or*/
    {done:false, value:SOMEVALUE}
    

    One way to access an object Iterator is with the:

    for ( var of object ) { }
    

    loop. Here is a (reasonably silly) example where we define an Iterator and then use it in such a loop to produce a string 1, 2, 3:

    "use strict";
    
    function count ( i ) {
      let n = 0;
      let I = {};
      I[Symbol.iterator] = function() {
         return { next: function() { return (n > i) ? {done:true}
                                                    : {done:false, value:n++} } } };
      let s = "";
      let c = "";
      for ( let i of I ) {       /* use the iterator we defined above */
          s += c + i;
          c = ", "
      }
      return s;
    }
    
    
    let s = count(3);
    console.log(s);
    
    0 讨论(0)
  • 2020-12-29 08:07

    Since this hasn't been mention yet arrays have higher-order functions built in.

    Map works like iterator that can only do a single pass.

    [1,2,3,4,5].map( function(input){ console.log(input); } );
    

    This code passes each element in the list into a function, in this case its a simple printer.

    1
    2
    3
    4
    5
    
    0 讨论(0)
  • This is my attempt (jsfiddle) for ECMAScript 262 5th edition (aka Javascript). (Uses for example Object.keys and Array.isArray)

    //Usage
    b=Iterator(a);
    while(b()){
      console.log(b.value);
    }
    

    The code:

    function Iterator(input,keys) {
      // Input:
      //  input : object|array
      //  keys   : array|undefined|boolean
      function my() {
        ++my.index;
        if (my.index >= my.keys.length) {
          my.index = my.keys.length -1;
          my.key = my.value = undefined;
          return false;
        }
        my.key = my.useIndex ? my.index : my.keys[my.index];
        my.value = my.input[my.key];
        return my.index < my.keys.length;
      }
      if (input === null || typeof input !== 'object') {
        throw new TypeError("'input' should be object|array");
      }
      if (
        !Array.isArray(keys)
        && (typeof keys !== 'undefined')
        && (typeof keys !== 'boolean')
        ) {
        throw new TypeError("'keys' should be array|boolean|undefined");
      }
      // Save a reference to the input object.
      my.input = input;
      if (Array.isArray(input)) {
        //If the input is an array, set 'useIndex' to true if 
        //the internal index should be used as a key.
        my.useIndex = !keys;
        //Either create and use a list of own properties,
        // or use the supplied keys
        // or at last resort use the input (since useIndex is true in that
        // case it is only used for the length)
        my.keys = keys===true ? Object.keys(input) : keys || input;
      } else {
        my.useIndex = false;
        my.keys = Array.isArray(keys) ? keys : Object.keys(input);
      }
      // Set index to before the first element.
      my.index = -1;
      return my;
    }
    

    Examples:

    function Person(firstname, lastname, domain) {
      this.firstname = firstname;
      this.lastname = lastname;
      this.domain = domain;
    }
    Person.prototype.type = 'Brillant';
    
    var list = [
      new Person('Paula','Bean','some.domain.name'),
      new Person('John','Doe','another.domain.name'),
      new Person('Johanna','Doe','yet.another.domain.name'),
    ];
    
    var a,b; 
    var data_array = ['A','B','C','D','E','F'];
    data_array[10]="Sparse";
    
    
    console.log('Iterate over own keys in an object, unknown order');
    a = Iterator(list[0]);
    while(a()) console.log("  ",a.key, a.value);
    
    console.log('Iterate over keys from anywhere, in specified order');
    a = Iterator(list[0], ['lastname','firstname','type']);
    while(a()) console.log("  ",a.key, a.value);
    
    console.log('Iterate over all values in an array');
    a = Iterator(list);
    while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
    
    
    //Some abusing, that works for arrays (if the iterator.keys is modified
    //it can also be used for objects)
    console.log('Add more entries to the array, reusing the iterator...');
    list.push(new Person('Another','Name','m.nu'));
    while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
    
    console.log('Reset index and print everything again...');
    a.index=-1; //Reset the index.
    while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
    
    //With arrays, if setting 'keys' to true it will only print the
    //elements that has values (If the array has more own enumerable values
    //they too will be included)
    console.log('Print sparce arrays...');
    a = Iterator(data_array,true);
    while(a()) console.log(a.key, a.value);
    
    0 讨论(0)
  • 2020-12-29 08:18

    Ok, the enumerable pattern is not a real iterator then.

    Is this (below) useful for you? It conforms to the sematics you gave at least. As usual there are tradeoffs to be made here and there, and I didn't think very hard when deciding this time :).
    And maybe you would like to be able to send in a number or two and iterate over a range in that way. But this could maybe be a start (there's support for iterating over hashes, arrays and strings).

    It's a whole demo page which runs itself and does some debug output, but the (possibly) interesting stuff is in the

    window.npup = (function() {
        [...]
    })();
    

    spot.

    Maybe it is just me who doesn't get it at all, but what would you use such a java-like Iterator for in a real situation?

    Best /npup

    <html>
    <head>
    <title>untitled</title>
    </head>
    
    <body>
        <ul id="output"></ul>
    
    
    <script type="text/javascript">
    window.log = (function (outputAreaId) {
        var myConsole = document.getElementById(outputAreaId);
        function createElem(color) {
            var elem = document.createElement('li');
            elem.style.color = color;
            return elem;
        }
        function appendElem(elem) {
            myConsole.appendChild(elem);
        }
        function debug(msg) {
            var elem = createElem('#888');
            elem.innerHTML = msg;
            appendElem(elem);
        }
        function error(msg) {
            var elem = createElem('#f88');
            elem.innerHTML = msg;
            appendElem(elem);
        }
        return {
            debug: debug
            , error: error
        };
    })('output');
    
    
    window.npup = (function () {
        // Array check as proposed by Mr. Crockford
        function isArray(candidate) {
            return candidate &&
                typeof candidate==='object' &&
                typeof candidate.length === 'number' &&
                typeof candidate.splice === 'function' &&
                !(candidate.propertyIsEnumerable('length'));
        }
        function dontIterate(collection) {
            // put some checks chere for stuff that isn't iterable (yet)
            return (!collection || typeof collection==='number' || typeof collection==='boolean');
        }
        function Iterator(collection) {
            if (typeof collection==='string') {collection = collection.split('');}
            if (dontIterate(collection)) {throw new Error('Oh you nasty man, I won\'t iterate over that ('+collection+')!');}
            var arr = isArray(collection);
            var idx = 0, top=0;
            var keys = [], prop;
            if (arr) {top = collection.length;}
            else {for (prop in collection) {keys.push(prop);}}
            this.next = function () {
                if (!this.hasNext()) {throw new Error('Oh you nasty man. I have no more elements.');}
                var elem = arr ? collection[idx] : {key:keys[idx], value:collection[keys[idx]]};
                ++idx;
                return elem;
            };
            this.hasNext = function () {return arr ? idx<=top : idx<=keys.length;};
        }
        return {Iterator: Iterator};
    })();
    
    var element;
    
    log.debug('--- Hash demo');
    var o = {foo:1, bar:2, baz:3, bork:4, hepp: {a:1,b:2,c:3}, bluff:666, bluff2:777};
    var iterator = new npup.Iterator(o);
    for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
        log.debug('got elem from hash: '+element.key+' => '+element.value);
        if (typeof element.value==='object') {
            var i2 = new npup.Iterator(element.value);
            for (var e2=i2.next(); i2.hasNext(); e2=i2.next()) {
                log.debug('&nbsp;&nbsp;&nbsp;&nbsp;# from inner hash: '+e2.key+' => '+e2.value);
            }
        }
    }
    log.debug('--- Array demo');
    var a = [1,2,3,42,666,777];
    iterator = new npup.Iterator(a);
    for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
        log.debug('got elem from array: '+ element);
    }
    log.debug('--- String demo');
    var s = 'First the pants, THEN the shoes!';
    iterator = new npup.Iterator(s);
    for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
        log.debug('got elem from string: '+ element);
    }
    log.debug('--- Emptiness demo');
    try {
        log.debug('Try to get next..');
        var boogie = iterator.next();
    }
    catch(e) {
        log.error('OW: '+e);
    }
    
    log.debug('--- Non iterables demo');
    try{iterator = new npup.Iterator(true);} catch(e) {log.error('iterate over boolean: '+e);}
    try{iterator = new npup.Iterator(6);} catch(e) {log.error('iterate over number: '+e);}
    try{iterator = new npup.Iterator(null);} catch(e) {log.error('iterate over null: '+e);}
    try{iterator = new npup.Iterator();} catch(e) {log.error('iterate over undefined: '+e);}
    
    </script>
    </body>
    </html>
    
    0 讨论(0)
  • 2020-12-29 08:21

    JQuery has the each() method: http://api.jquery.com/jQuery.each/

    but probably there's something similar even in other libraries such as Moo or Dojo.

    Javascript 1.7 implements the Iterator function: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators

    0 讨论(0)
  • 2020-12-29 08:22

    I'm still a learner of js.class. Though being close to Ruby, helps me.

    http://jsclass.jcoglan.com/enumerable.html

    MarkT

    0 讨论(0)
提交回复
热议问题