Класс JavaScript Iterator - PullRequest
       25

Класс JavaScript Iterator

16 голосов
/ 15 апреля 2010

Знаете ли вы библиотеку JavaScript, которая реализует общий класс Iterator для коллекций (будь то массивы или некоторые абстрактные Enumerable) с полным набором функций, таких как Google Common или Apache Commons

Редактировать: Enumerable#each не является классом Iterator. Я ищу Итератор, который позволил бы нам написать что-то вроде:

var iterator = new Iterator(myCollection);
for (var element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
    // iterator 
}

Редактировать: mamoo напомнил нам о реализации Iterator в JavaScript Mozilla 1.7 . Таким образом, цель сейчас состоит в том, чтобы найти реализацию этой функции Iterator в Javascript 1.5 (ECMA 4).

Edit2: зачем использовать итератор, когда библиотеки (и ECMA 5) предоставляют метод each? Во-первых, потому что each обычно портится с this, потому что обратный вызов * call -обеден (поэтому each принимает второй аргумент в Prototype). Тогда, потому что люди намного лучше знакомы с конструкцией for(;;), чем с конструкцией .each(callback) (по крайней мере, в моей области). Наконец, потому что итератор может перебирать простые объекты (см. JavaScript 1.7).

Edit3: я принял ответ npup, но вот мой выстрел в него:

function Iterator(o, keysOnly) {
    if (!(this instanceof arguments.callee))
      return new arguments.callee(o, keysOnly);
    var index = 0, keys = [];
    if (!o || typeof o != "object") return;
    if ('splice' in o && 'join' in o) {
        while(keys.length < o.length) keys.push(keys.length);
    } else {
        for (p in o) if (o.hasOwnProperty(p)) keys.push(p);
    }
    this.next = function next() {
        if (index < keys.length) {
            var key = keys[index++];
            return keysOnly ? key : [key, o[key]];
        } else throw { name: "StopIteration" };
    };
    this.hasNext = function hasNext() {
        return index < keys.length;
    };
}



var lang = { name: 'JavaScript', birthYear: 1995 };  
var it = Iterator(lang);
while (it.hasNext()) {
    alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown  


var langs = ['JavaScript', 'Python', 'C++'];  
var it = Iterator(langs);
while (it.hasNext()) {
    alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown  

Ответы [ 7 ]

5 голосов
/ 15 апреля 2010

Хорошо, тогда перечислимый шаблон не является реальным итератором.

Это (ниже) полезно для вас? Это соответствует семантике, которую вы дали, по крайней мере. Как обычно, есть компромиссы, которые нужно сделать тут и там, и я не особо задумывался, решая на этот раз:).
И, возможно, вы хотели бы иметь возможность отправить число или два и перебрать диапазон таким образом. Но это может быть началом (есть поддержка перебора хешей, массивов и строк).

Это целая демонстрационная страница, которая запускается сама по себе и выполняет отладочный вывод, но (возможно) интересный материал находится в

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

место.

Может быть, только я вообще не понимаю, но для чего вы использовали бы подобный java-итератор в реальной ситуации?

Лучший / 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>
5 голосов
/ 15 апреля 2010

JQuery имеет метод each (): http://api.jquery.com/jQuery.each/

но, возможно, что-то подобное есть даже в других библиотеках, таких как Moo или Dojo.

Javascript 1.7 реализует функцию Iterator: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators

3 голосов
/ 10 февраля 2016

За время, прошедшее с тех пор, как этот вопрос был задан, JavaScript добавил фактические Итераторы . Некоторые встроенные типы, такие как Array , Map и String , теперь имеют стандартное поведение итерации, но вы можете добавить свой собственный для любого объекта, включив next() функция, которая возвращает один из двух объектов:

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

Одним из способов доступа к объекту Iterator является использование:

for ( var of object ) { }

петля. Вот (довольно глупый) пример, в котором мы определяем итератор, а затем используем его в таком цикле для создания строки 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);
3 голосов
/ 27 июля 2012

Это моя попытка ( jsfiddle ) для ECMAScript 262 5-е издание (он же Javascript). (Используется, например, Object.keys и Array.isArray)

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

Код:

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;
}

Примеры:

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);
1 голос
/ 05 января 2013

Поскольку это еще не было упомянуто, в массивы встроены функции высшего порядка.

Карта работает как итератор, который может выполнить только один проход.

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

Этот код передает каждый элемент списка в функцию, в данном случае это простой принтер.

1
2
3
4
5
1 голос
/ 13 января 2012

Я все еще учусь в js.class. Хотя быть рядом с Руби, помогает мне.

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

Markt

1 голос
/ 15 апреля 2010

Я использовал LINQ to Javascript в нескольких проектах.

http://jslinq.codeplex.com/Wikipage

var myList = [
            {FirstName:"Chris",LastName:"Pearson"},
            {FirstName:"Kate",LastName:"Johnson"},
            {FirstName:"Josh",LastName:"Sutherland"},
            {FirstName:"John",LastName:"Ronald"},
            {FirstName:"Steve",LastName:"Pinkerton"}
            ];

var exampleArray = JSLINQ(myList)
                   .Where(function(item){ return item.FirstName == "Chris"; })
                   .OrderBy(function(item) { return item.FirstName; })
                   .Select(function(item){ return item.FirstName; });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...