Iterar sobre chaves de objeto no node.js


139

Desde o Javascript 1.7, existe um objeto Iterator , que permite:

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();

existe algo parecido com isto no node.js?

Agora eu estou 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();
        }
    };
}

mas isso gera muita sobrecarga ao armazenar todas as chaves de objeto k.



2
Que sobrecarga? Quantas chaves e iteradores você possui? Se o produto for inferior a 1 milhão, ignore essa 'ineficiência'.
C69 16/09

@jcolebrand φ: Parece que createNodeIteratoré para elementos DOM, eu nem tenho um DOM;) @ c69: eu armazeno todos os dados no keysobjeto e o valueé apenas definido como 1(cerca de 20 MB em chaves de 700k), de fato, para agora eu só estou ignorando esse 'sobrecarga', mas eu preferiria uma solução melhor :)
Stewe

Eu vi isso como uma classe deve ser sujada com ;-)
jcolebrand

Respostas:


246

O que você deseja é uma iteração lenta sobre um objeto ou matriz. Isso não é possível no ES5 (portanto, não é possível no node.js). Nós vamos conseguir isso eventualmente.

A única solução é encontrar um módulo de nó que estenda a V8 para implementar iteradores (e provavelmente geradores). Não encontrei nenhuma implementação. Você pode olhar o código-fonte spidermonkey e tentar escrevê-lo em C ++ como uma extensão V8.

Você pode tentar o seguinte, no entanto, também carregará todas as chaves na memória

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

No entanto, como Object.keysé um método nativo, ele pode permitir uma melhor otimização.

Referência

Como você pode ver, Object.keys é significativamente mais rápido. Se o armazenamento real da memória é mais otimizado é uma questão 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);
});

! Obrigado, isso melhora o meu iterador um pouco :) (atualizado o código), mas infelizmente o problema persistir memória :( E eu não posso usar forEachuma vez que cada passo iteração deve ser chamado a partir de um assíncrono setTimeout.
Stewe

@stewe adicionou umasync.forEach
Raynos

Obrigado pelo esclarecimento! Provavelmente tentarei a abordagem de extensão c ++.
stewe

2
@stewe se você conseguir escrevê-lo, publicá-lo no github e deixar um link para ele em uma resposta aqui ou um comentário o /
Raynos

@stewe sobre essa extensão C ++, você a criou?
Raynos

22

Lembre-se também de que você pode passar um segundo argumento para a .forEach()função que especifica o objeto a ser usado como thispalavra - chave.

// 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);

5
boa adição ao segmento, mas ... por que mostrar a chave do objeto que está sendo passado como algo chamado "elemento" e o enumerador da matriz de chaves chamado "chave" ?! Posso sugerir que você atualizar seu código de exemplo para usoObject.keys(myObject).forEach(function(key, index, arrayOfKeys) {
Andy Lorenz

4

Para uma iteração simples de chave / valores, às vezes bibliotecas como underscorejs podem ser seus amigos.

const _ = require('underscore');

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

Somente para referência


Funcionou para mim. Não conheço underscorejs. Eu usei essa função da lodashbiblioteca.
Neerali Acharya

3

Eu sou novo no node.js (cerca de 2 semanas), mas acabei de criar um módulo que reporta recursivamente ao console o conteúdo de um objeto. Ele listará tudo ou procurará um item específico e, em seguida, detalhará uma profundidade específica, se necessário.

Talvez você possa personalizar isso para atender às suas necessidades. Mantenha simples! Por que complicar? ...

'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");

0

ajuste o código dele:

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);
    });
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.