2010-04-15 6 views
15

¿Conoce una biblioteca de JavaScript que implemente una clase genérica Iterator para colecciones (ya sean matrices o algún enumerativo abstracto) con un conjunto completo de características, como Google Common o Apache Commons?JavaScript Iterator Clase

Editar: Enumerable#each no es una clase Iterator. Busco un iterador, algo que nos permita escribir algo como:

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

Editar: Mamoo nos ha recordado la puesta en práctica de iteración de Mozilla's Javascript 1.7. Entonces, el objetivo ahora es encontrar una implementación de esta función Iterator en Javascript 1.5 (ECMA 4).

Edit2: ¿Por qué utilizar un iterador cuando las bibliotecas (y ECMA 5) proporcionan un método each? En primer lugar, porque each generalmente entra en conflicto con this porque la devolución de llamada es call -ed (es por eso que each acepta un segundo argumento en Prototype). Entonces, porque las personas están mucho más familiarizadas con el constructo for(;;) que con el constructo .each(callback) (al menos, en mi campo). Por último, porque un iterador puede iterar sobre objetos planos (ver JavaScript 1.7).

Edit3: Me aceptó anwser de npup, pero aquí está mi tiro en él:

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 
+0

Claro, es por eso que esperamos 15 años para que se incluya en el idioma. – Alsciende

+2

@stereofrog, muchas bibliotecas y scripts actualmente usan llamadas a funciones asincrónicas (bueno, hasta que los WebWorkers sean generalmente compatibles, pero incluso entonces) y de momento, ¿cómo se supone iterar a través de datos de forma asincrónica (de forma no bloqueante) con un simple .each() función o una declaración "para en"? Iteradores son la mejor solución; se pueden pasar a través de funciones asíncronas para reanudar iteraciones fácilmente y sin importar la implementación subyacente. (Este es el patrón de iterador después de todo). Javascript no es solo un lenguaje funcional ... sino que incluso el lenguaje funcional * do * tiene iteradores ... –

+0

En la edición 2, cuando mencionaste el método 'each' de ES5, ¿te refieres al método' .forEach() '? – dcorking

Respuesta

6

Ok, el patrón enumerables no es un verdadero iterador a continuación.

¿Es esto (abajo) útil para usted? Se ajusta a las sematics que diste al menos. Como de costumbre, hay compensaciones para hacer aquí y allá, y no pensé demasiado al momento de decidir esta vez :).
Y tal vez le gustaría poder enviar un número o dos e iterar sobre un rango de esa manera. Pero esto podría ser un comienzo (hay soporte para iterar sobre hash, matrices y cadenas).

Es una página de demostración entera que se extiende a sí mismo y hace algo de salida de depuración, pero el (posiblemente) cosas interesantes está en el lugar

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

.

Tal vez sea solo yo quien no lo obtenga en absoluto, pero ¿para qué usaría un iterador de tipo java en una situación real?

Mejor /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

Buen trabajo. Me gusta cómo iteraste sobre cadenas, ¿por qué no? Para una justificación de la pregunta, voy a editar mi pregunta. – Alsciende

+1

en Javascript, un iterador es útil si se necesita iterar a través de todos los elementos de una matriz, pero de manera asíncrona (recorrer los primeros n elementos, luego reanudar después de un retraso desde el elemento n + 1, etc.) –

1

he utilizado LINQ a Javascript en algunos proyectos.

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; }); 
+1

LINQ se ve muy bien, pero ¿cómo es relevante para iteradores? Parece que está hecho para consultar conjuntos de datos. – Alsciende

+0

LINQ es una biblioteca de programación funcional escrita en estilo DSL por lo que se parece a SQL. – Dan

3

Este es mi intento (jsfiddle) para ECMAScript 262 5ª edición (también conocido como Javascript). (Usos de ejemplo Object.keys y Array.isArray)

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

el código:

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

Ejemplos:

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

Dado que esto no ha sido todavía mención matrices tienen de mayor funciones de orden integradas.

Map funciona como un iterador que solo puede hacer una sola pasada.

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

Este código pasa cada elemento de la lista a una función, en este caso es una impresora simple.

1 
2 
3 
4 
5 
3

En el tiempo transcurrido desde esta pregunta se le preguntó JavaScript ha añadido real Iterators. Algunos tipos incorporados, tales como Array, Map y String ahora tienen un comportamiento iteración defecto, pero se puede añadir su propia a cualquier objeto mediante la inclusión de una función next() que devuelve uno de dos objetos:

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

Uno forma de acceder a un objeto Iterator es con el:

for (var of object) { } 

loop. Aquí es un (bastante tonta) ejemplo en el que se define un iterador y luego usarlo en un bucle como para producir una cadena 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);