Iterar sobre claves de objeto en node.js

139

Desde Javascript 1.7 hay un objeto Iterator , que permite esto:

var a={a:1,b:2,c:3};
var it=Iterator(a);

function iterate(){
    try {  
        console.log(it.next());
        setTimeout(iterate,1000);
    }catch (err if err instanceof StopIteration) {  
        console.log("End of record.\n");  
    } catch (err) {  
        console.log("Unknown error: " + err.description + "\n");  
    }  

}
iterate();

¿hay algo como esto en node.js?

En este momento estoy usando:

function Iterator(o){
    /*var k=[];
    for(var i in o){
        k.push(i);
    }*/
    var k=Object.keys(o);
    return {
        next:function(){
            return k.shift();
        }
    };
}

pero eso genera muchos gastos generales al almacenar todas las claves de objeto k.

stewe
fuente
¿Has visto esto? ejohn.org/blog/unimpressed-by-nodeiterator
jcolebrand
2
¿Qué gastos generales? ¿Cuántas teclas e iteradores tienes? Si su producto es inferior a 1 millón, simplemente ignore esta 'ineficiencia'.
c69
@jcolebrand φ: Parece que createNodeIteratores para elementos DOM, ni siquiera tengo un DOM;) @ c69: almaceno todos los datos en keysel objeto y valuesimplemente se establece en 1(aproximadamente 20 MB en 700k claves), de hecho, para ahora solo estoy ignorando esta 'sobrecarga', pero preferiría una mejor solución :)
stewe
Lo vi como una clase con la que
meterse

Respuestas:

246

Lo que quieres es una iteración perezosa sobre un objeto o matriz. Esto no es posible en ES5 (por lo tanto, no es posible en node.js). Obtendremos esto eventualmente.

La única solución es encontrar un módulo de nodo que extienda V8 para implementar iteradores (y probablemente generadores). No pude encontrar ninguna implementación. Puede mirar el código fuente de spidermonkey e intentar escribirlo en C ++ como una extensión V8.

Puede intentar lo siguiente, sin embargo, también cargará todas las claves en la memoria

Object.keys(o).forEach(function(key) {
  var val = o[key];
  logic();
});

Sin embargo, dado que Object.keyses un método nativo, puede permitir una mejor optimización.

Punto de referencia

Como puede ver Object.keys es significativamente más rápido. Si el almacenamiento de memoria real es más óptimo es una cuestión diferente.

var async = {};
async.forEach = function(o, cb) {
  var counter = 0,
    keys = Object.keys(o),
    len = keys.length;
  var next = function() {
    if (counter < len) cb(o[keys[counter++]], next);
  };
  next();
};

async.forEach(obj, function(val, next) {
  // do things
  setTimeout(next, 100);
});
Raynos
fuente
! Gracias, esto mejora mi iterador un poco :) (actualizado el código) pero por desgracia la cuestión sigue siendo de memoria :( y no puedo usar forEachya que cada paso de la iteración debe ser invocado desde un asíncrono setTimeout.
Stewe
@stewe agregó unasync.forEach
Raynos
¡Gracias por la aclaración! Probablemente probaré el enfoque de extensión de c ++.
stewe
2
@stewe si logras escribirlo, publícalo en github y deja un enlace en una respuesta aquí o un comentario o /
Raynos
@stewe sobre esa extensión de C ++, ¿la creaste?
Raynos
22

Recuerde también que puede pasar un segundo argumento a la .forEach()función que especifica el objeto para usar como thispalabra clave.

// myOjbect is the object you want to iterate.
// Notice the second argument (secondArg) we passed to .forEach.
Object.keys(myObject).forEach(function(element, key, _array) {
  // element is the name of the key.
  // key is just a numerical value for the array
  // _array is the array of all the keys

  // this keyword = secondArg
  this.foo;
  this.bar();
}, secondArg);
barista aficionado
fuente
55
buena adición al hilo, pero ... ¿por qué demonios mostrar la clave del objeto que se pasa como algo llamado "elemento", y el enumerador de la matriz de claves llamada "clave"?! ¿Puedo sugerirle que actualice su muestra de código para usarObject.keys(myObject).forEach(function(key, index, arrayOfKeys) {
Andy Lorenz
4

Para una simple iteración de clave / valores, a veces las bibliotecas como underscorejs pueden ser su amigo.

const _ = require('underscore');

_.each(a, function (value, key) {
    // handle
});

solo para referencia

H6.
fuente
Funcionó para mi. No sé acerca underscorejs. Usé esta función de la lodashbiblioteca.
Neerali Acharya
3

Soy nuevo en node.js (aproximadamente 2 semanas), pero acabo de crear un módulo que informa recursivamente a la consola el contenido de un objeto. Enumerará todo o buscará un artículo específico y luego profundizará por una profundidad dada si es necesario.

Quizás pueda personalizar esto para satisfacer sus necesidades. ¡Mantenlo simple! ¿Por qué complicarse? ...

'use strict';

//console.log("START: AFutils");

// Recusive console output report of an Object
// Use this as AFutils.reportObject(req, "", 1, 3); // To list all items in req object by 3 levels
// Use this as AFutils.reportObject(req, "headers", 1, 10); // To find "headers" item and then list by 10 levels
// yes, I'm OLD School!  I like to see the scope start AND end!!!  :-P
exports.reportObject = function(obj, key, level, deep) 
{
    if (!obj)
    { 
        return;
    }

    var nextLevel = level + 1;

    var keys, typer, prop;
    if(key != "")
    {   // requested field
        keys = key.split(']').join('').split('[');
    }
    else
    {   // do for all
        keys = Object.keys(obj);
    }
    var len = keys.length;
    var add = "";
    for(var j = 1; j < level; j++)
    {
        // I would normally do {add = add.substr(0, level)} of a precreated multi-tab [add] string here, but Sublime keeps replacing with spaces, even with the ["translate_tabs_to_spaces": false] setting!!! (angry)
        add += "\t";
    }

    for (var i = 0; i < len; i++) 
    {
        prop = obj[keys[i]];
        if(!prop)
        {
            // Don't show / waste of space in console window...
            //console.log(add + level + ": UNDEFINED [" + keys[i] + "]");
        }
        else
        {
            typer = typeof(prop);
            if(typer == "function")
            {
                // Don't bother showing fundtion code...
                console.log(add + level + ": [" + keys[i] + "] = {" + typer + "}");
            }
            else
            if(typer == "object")
            {
                console.log(add + level + ": [" + keys[i] + "] = {" + typer + "}");
                if(nextLevel <= deep)
                {
                    // drop the key search mechanism if first level item has been found...
                    this.reportObject(prop, "", nextLevel, deep); // Recurse into
                }
            }
            else
            {
                // Basic report
                console.log(add + level + ": [" + keys[i] + "] = {" + typer + "} = " + prop + ".");
            }
        }
    }
    return ;
};

//console.log("END: AFutils");
Andrew Foster, alias Sheff
fuente
0

ajustar su código:

Object.prototype.each = function(iterateFunc) {
        var counter = 0,
keys = Object.keys(this),
currentKey,
len = keys.length;
        var that = this;
        var next = function() {

            if (counter < len) {
                currentKey = keys[counter++];
                iterateFunc(currentKey, that[currentKey]);

                next();
            } else {
                that = counter = keys = currentKey = len = next = undefined;
            }
        };
        next();
    };

    ({ property1: 'sdsfs', property2: 'chat' }).each(function(key, val) {
        // do things
        console.log(key);
    });
Lindy
fuente