2010-04-15 5 views
15

Connaissez-vous une bibliothèque JavaScript qui implémente une classe générique Iterator pour les collections (que ce soit des tableaux ou un résumé Enumerable) avec un ensemble complet de fonctionnalités, telles que Google Common ou Apache Commons?JavaScript Iterator Class

Modifier: Enumerable#each n'est pas une classe Iterator. Je suis à la recherche d'un Iterator, ce qui nous permettrait d'écrire quelque chose comme:

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

Edit: Mamoo nous a rappelé la mise en œuvre itérateur Mozilla's Javascript 1.7. L'objectif est maintenant de trouver une implémentation de cette fonction Iterator dans Javascript 1.5 (ECMA 4).

Édition2: Pourquoi utiliser un itérateur lorsque les bibliothèques (et ECMA 5) fournissent une méthode each? Tout d'abord, parce que each est généralement en désordre avec this car le rappel est call -ed (c'est pourquoi each accepte un second argument dans Prototype). Puis, parce que les gens sont beaucoup plus familiers avec la construction for(;;) qu'avec la construction .each(callback) (au moins, dans mon domaine). Enfin, parce qu'un itérateur peut parcourir des objets simples (voir JavaScript 1.7).

Edit3: J'ai accepté le anwser de npup, mais voici mon coup à elle:

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

Bien sûr, c'est pourquoi nous avons attendu 15 ans pour l'avoir inclus dans la langue. – Alsciende

+2

@stereofrog, de nombreuses librairies et scripts utilisent aujourd'hui des appels de fonctions asynchrones (enfin, jusqu'à ce que WebWorkers soient généralement supportés, mais encore) et pour l'instant, comment suppose-t-on d'itérer de manière asynchrone (de manière non bloquante) Une fonction .each() ou une instruction "for in"? Les itérateurs sont la meilleure solution. ils peuvent être passés à travers des fonctions asynchrones pour reprendre les itérations facilement et indépendamment de l'implémentation sous-jacente. (C'est le modèle d'itérateur après tout). Javascript n'est pas seulement un langage fonctionnel ... mais même un langage fonctionnel * do * a des itérateurs ... –

+0

Dans l'édition 2, quand vous avez mentionné la méthode 'each' d'ES5, vouliez-vous dire la méthode' .forEach() '? – dcorking

Répondre

6

Ok, le modèle dénombrable est pas un vrai iterator alors.

Est-ce que (ci-dessous) vous est utile? Il est conforme à la sémantique que vous avez donné au moins. Comme d'habitude, il y a des compromis à faire ici et là, et je n'ai pas réfléchi très fort en décidant cette fois :).
Et peut-être que vous aimeriez être en mesure d'envoyer un nombre ou deux et itérer sur une plage de cette façon. Mais cela pourrait peut-être être un début (il y a un support pour l'itération sur les hachages, les tableaux et les chaînes).

Il est une page de démonstration entière qui s'exécute et fait une sortie de débogage, mais le (peut-être) des choses intéressantes est dans le

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

.

Peut-être que c'est juste moi qui ne comprends pas du tout, mais que feriez-vous pour utiliser un tel Iterator comme dans une situation réelle?

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

Excellent travail. J'aime comment tu as ité au cours des cordes, pourquoi pas? Pour une justification de la question, je vais éditer ma question. – Alsciende

+1

en Javascript, un itérateur est utile si l'on a besoin de parcourir tous les éléments d'un tableau, mais de façon asynchrone (boucler les n premiers éléments, puis reprendre après un délai du n + 1 ème élément, etc.) –

1

Ive utilisé LINQ to Javascript dans quelques projets.

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 est superbe, mais en quoi cela concerne-t-il les itérateurs? Il semble que ce soit fait pour interroger des ensembles de données. – Alsciende

+0

LINQ est une bibliothèque de programmation fonctionnelle écrite dans un style DSL, ce qui explique pourquoi elle ressemble à SQL. – Dan

3

Ceci est ma tentative (jsfiddle) pour ECMAScript 262 5ème édition (alias Javascript). (Utilise par exemple Object.keys et Array.isArray)

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

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

Exemples:

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

Comme il n'a pas été question encore des tableaux sont cadres fonctions de commande intégrées.

La carte fonctionne comme un itérateur qui ne peut effectuer qu'un seul passage.

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

Ce code passe chaque élément de la liste dans une fonction, dans ce cas c'est une simple imprimante.

1 
2 
3 
4 
5 
3

Depuis que cette question a été posée, JavaScript a été ajouté Iterators. Certains types intégrés, tels que Array, Map et String ont maintenant un comportement d'itération par défaut, mais vous pouvez ajouter votre propre à tout objet en incluant une fonction next() qui retourne l'un des deux objets:

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

One façon d'accéder à un objet Iterator est avec le:

for (var of object) { } 

boucle. Voici un (assez stupide) par exemple où nous définissons un itérateur puis l'utiliser dans une telle boucle pour produire une chaîne 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);