mark - set vs array javascript
Clase de iterador de JavaScript (7)
Como esto no se ha mencionado, las matrices tienen funciones de orden superior integradas.
El mapa 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
¿Conoce una biblioteca de JavaScript que implementa una clase de iterador genérico para colecciones (ya sea matrices o algún Enumerable abstracto) con un conjunto completo de características, como Google Common o Apache Commons ?
Edición: Enumerable#each
una no es una clase de iterador. Estoy buscando 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
}
Edición: mamoo nos recordó la implementación del iterador en Javascript 1.7 de Mozilla . Entonces, el objetivo ahora es encontrar una implementación de esta función de iterador en Javascript 1.5 (ECMA 4).
Edit2: ¿Por qué usar un iterador cuando las bibliotecas (y ECMA 5) proporcionan un método para each
? Primero, porque each
generalmente se mete con this
porque la devolución de llamada es call
(es por eso que each
acepta un segundo argumento en Prototype). Entonces, porque la gente está mucho más familiarizada con el constructo for(;;)
que con el constructo .each(callback)
(al menos, en mi campo). Por último, porque un iterador puede iterar sobre objetos simples (ver JavaScript 1.7).
Edit3: acepté la respuesta de npup, pero aquí está mi oportunidad:
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
Desde que se hizo esta pregunta, JavaScript ha agregado Iterators reales. Algunos tipos incorporados, como Array , Map y String ahora tienen un comportamiento de iteración predeterminado, pero puedes agregar el tuyo a cualquier objeto incluyendo una función next()
que devuelve uno de los dos objetos:
{done:true} /*or*/
{done:false, value:SOMEVALUE}
Una forma de acceder a un iterador de objetos es con:
for ( var of object ) { }
lazo. Aquí hay un ejemplo (razonablemente tonto) donde definimos un iterador y luego lo usamos en dicho bucle 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);
Este es mi intento ( jsfiddle ) para ECMAScript 262 5ª edición (también conocido como Javascript). (Utiliza por 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);
He usado LINQ to 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; });
JQuery tiene el método each (): http://api.jquery.com/jQuery.each/
pero probablemente hay algo similar incluso en otras bibliotecas como Moo o Dojo.
Javascript 1.7 implementa la función Iterador: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators
Ok, el patrón enumerable no es un iterador real entonces.
¿Es esto (abajo) útil para ti? Se ajusta a la sematica que le diste al menos. Como de costumbre, hay que hacer concesiones aquí y allá, y no pensé mucho al decidir esta vez :).
Y tal vez le gustaría poder enviar un número o dos e iterar en un rango de esa manera. Pero esto podría ser un comienzo (hay soporte para iterar sobre hashes, matrices y cadenas).
Es una página de demostración completa que se ejecuta por sí misma y hace algunos resultados de depuración, pero las cosas (posiblemente) interesantes están en el
window.npup = (function() {
[...]
})();
lugar.
Tal vez sea solo yo quien no lo comprenda, pero ¿para qué usaría un iterador similar a 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('' # 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>
Todavía soy un aprendiz de js.class. Aunque estar cerca de Ruby, me ayuda.
http://jsclass.jcoglan.com/enumerable.html
MarkT