Cómo recorrer un objeto JavaScript simple con los objetos como miembros

1701

¿Cómo puedo recorrer todos los miembros de un objeto JavaScript, incluidos los valores que son objetos?

Por ejemplo, ¿cómo podría recorrer esto (accediendo a "tu_nombre" y "tu_mensaje" para cada uno)?

var validation_messages = {
    "key_1": {
        "your_name": "jimmy",
        "your_msg": "hello world"
    },
    "key_2": {
        "your_name": "billy",
        "your_msg": "foo equals bar"
    }
}
0
2190
for (var key in validation_messages) {
    // skip loop if the property is from prototype
    if (!validation_messages.hasOwnProperty(key)) continue;

    var obj = validation_messages[key];
    for (var prop in obj) {
        // skip loop if the property is from prototype
        if (!obj.hasOwnProperty(prop)) continue;

        // your code
        alert(prop + " = " + obj[prop]);
    }
}
4
  • dieciséis
    Internet Explorer no está de acuerdo ( suspiro ), dice "El objeto no admite esta propiedad o método" cuando hace obj [prop]. Todavía tengo que encontrar una solución a esto. 21/12/11 a las 12:02
  • 3
    @MildFuzz en realidad tiene sentido si considera que los objetos JS no necesariamente tienen claves numéricas. No puedes simplemente iterar a través de un objeto. JS for ines muy similar a un tradicional foreach. 28 de febrero de 2013 a las 5:16
  • 6
    for ... in es una buena solución, pero si usa promesas en el bucle for (), tenga cuidado, porque si crea una var en el bucle, no puede usarla en la función de promesa 'then. Su var en el bucle existe solo una vez, por lo que tiene en todas las funciones de entonces lo mismo, incluso el último valor. Si tiene ese problema, intente "Object.keys (obj) .forEach" o mi respuesta a continuación.
    Biber
    22/11/2016 a las 21:15
  • 1
    hasOwnProperty casi siempre es redundante para los navegadores modernos (IE9 +).
    Filyus
    5/06/20 a las 10:14
842

En ECMAScript 5, puede combinar Object.keys()y Array.prototype.forEach():

var obj = {
  first: "John",
  last: "Doe"
};

//
//	Visit non-inherited enumerable keys
//
Object.keys(obj).forEach(function(key) {

  console.log(key, obj[key]);

});
11
  • 38
    +1 para la brevedad del código, pero aparentemente no funciona tan eficientemente como para sorprendentemente. JSPerf - para in vs Object.keys 4 de septiembre de 2012 a las 12:33
  • 7
    Tenga cuidado con este error utilizando este enfoque: "TypeError: Object.keys llamado en non-object". El for ... in ... hasOwnPropertypatrón se puede invocar en cualquier cosa, hasta donde yo sé (objeto, matriz, nulo, indefinido, verdadero, falso, primitivo numérico, objetos). 12/10/12 a las 2:17
  • 4
    @ techiev2 esas pruebas nunca fueron válidas. Vea mis actualizados para el estado actual de rendimiento: jsperf.com/objdir/20 16/10/2014 a las 10:39
  • 5
    @ techiev2: no es lo Object.keys()que lo hace lento, ¡es más bien el forEach()acceso repetido a .length! Si usa un forbucle clásico en su lugar, es casi el doble de rápido que for..in+ hasOwnProperty()en Firefox 33. 5 de noviembre de 2014 a las 23:50
  • 3
    @ techiev2 eso no es sorprendente en absoluto, el método de Axel Rauschmayer está usando keysfunción y forEachfunción y necesita analizar la función anónima y luego llama a la función anónima en cada elemento del bucle forEach. Si sabe de programación, comprenderá que todo este análisis y las llamadas a funciones están tomando mucho más tiempo que una solución nativa como el bucle de estructura for. 23 de septiembre de 2017 a las 12:03
391

El problema con esto

for (var key in validation_messages) {
   var obj = validation_messages[key];
   for (var prop in obj) {
      alert(prop + " = " + obj[prop]);
   }
}

es que también recorrerás el prototipo del objeto primitivo.

Con este lo evitarás:

for (var key in validation_messages) {
   if (validation_messages.hasOwnProperty(key)) {
      var obj = validation_messages[key];
      for (var prop in obj) {
         if (obj.hasOwnProperty(prop)) {
            alert(prop + " = " + obj[prop]);
         }
      }
   }
}
3
  • 47
    En resumen: revise hasOwnPropertydentro de sus for- inbucles. 21/11/12 a las 5:11
  • 62
    Tenga en cuenta que esto solo es necesario si su objeto TIENE métodos de prototipo. Por ejemplo, si el objeto por el que está navegando es solo un objeto JSON, no necesitará esta comprobación. 29 de diciembre de 2013 a las 19:46
  • 7
    @rednaw Para estar seguro, utilizo ese cheque porque Object.prototype se puede modificar. Ninguna secuencia de comandos en su sano juicio haría eso, pero no puede controlar qué secuencias de comandos podrían ejecutarse en su página mediante extensiones de navegador locas. Las extensiones del navegador se ejecutan en su página (en la mayoría de los navegadores) y pueden causar problemas extraños (por ejemplo, ¡establezca window.setTimeout en null!).
    robocat
    5 de diciembre de 2014 a las 2:42
391

En ES6 / 2015 puede recorrer un objeto como este (usando la función de flecha ):

Object.keys(myObj).forEach(key => {
  console.log(key);        // the name of the current key.
  console.log(myObj[key]); // the value of the current key.
});

JS Bin

En ES7 / 2016 puede usar en Object.entrieslugar de Object.keysy recorrer un objeto como este:

Object.entries(myObj).forEach(([key, val]) => {
  console.log(key); // the name of the current key.
  console.log(val); // the value of the current key.
});

Lo anterior también funcionaría como una sola línea :

Object.entries(myObj).forEach(([key, val]) => console.log(key, val));

jsbin

En caso de que también desee recorrer objetos anidados, puede usar una función recursiva (ES6):

const loopNestedObj = obj => {
  Object.keys(obj).forEach(key => {
    if (obj[key] && typeof obj[key] === "object") loopNestedObj(obj[key]); // recurse.
    else console.log(key, obj[key]); // or do something with key and val.
  });
};

JS Bin

Lo mismo que la función anterior, pero con ES7 en Object.entries() lugar de Object.keys():

const loopNestedObj = obj => {
  Object.entries(obj).forEach(([key, val]) => {
    if (val && typeof val === "object") loopNestedObj(val); // recurse.
    else console.log(key, val); // or do something with key and val.
  });
};

Aquí recorremos los valores de cambio de objetos anidados y devolvemos un nuevo objeto de una vez usando Object.entries()combinado con Object.fromEntries()( ES10 / 2019 ):

const loopNestedObj = obj =>
  Object.fromEntries(
    Object.entries(obj).map(([key, val]) => {
      if (val && typeof val === "object") [key, loopNestedObj(val)]; // recurse
      else [key, updateMyVal(val)]; // or do something with key and val.
    })
  );

Otra forma de recorrer objetos es usando for ... in y for ... of . Vea la respuesta bien escrita de vdegenne .

2
  • 2
    para su ES7 usando el ejemplo de Object.entries, debe envolver los parámetros de la función de flecha [key, val] entre paréntesis como: `Object.entries (myObj) .forEach (([key, val]) => {/ * declaraciones * /}
    puiu
    2 mar 17 a las 17:10
  • 8
    Creo que sería útil agregar el hecho de que Object.entries y Object.keys no iteran sobre el prototipo, que es la gran diferencia entre este y el for in. 28 de marzo de 2017 a las 19:06
98

Usando Underscore.js_.each :

_.each(validation_messages, function(value, key){
    _.each(value, function(value, key){
        console.log(value);
    });
});
1
  • 4
    Gracias Tim, el uso de subrayado es definitivamente bueno para tener una opción rápida y limpia. 21/10/12 a las 22:18
59

Si usa la recursividad, puede devolver propiedades de objeto de cualquier profundidad.

function lookdeep(object){
    var collection= [], index= 0, next, item;
    for(item in object){
        if(object.hasOwnProperty(item)){
            next= object[item];
            if(typeof next== 'object' && next!= null){
                collection[index++]= item +
                ':{ '+ lookdeep(next).join(', ')+'}';
            }
            else collection[index++]= [item+':'+String(next)];
        }
    }
    return collection;
}

//example

var O={
    a:1, b:2, c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
};
var lookdeepSample= 'O={'+ lookdeep(O).join(',\n')+'}';


/*  returned value: (String)
O={
    a:1, 
    b:2, 
    c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
}

*/
1
  • 4
    Tenga cuidado con los bucles, como llamar a esto en un nodo DOM. 12/10/12 a las 2:35
53

This answer is an aggregate of the solutions that were provided in this post with some performance feedbacks. I think there is two use cases and the OP didn't mention if he needs to access the keys in order use them during the loop process.

I. Es necesario acceder a las claves

✔ El enfoque ofyObject.keys

let k;
for (k of Object.keys(obj)) {

    /*        k : key
     *   obj[k] : value
     */
}

✔ El inenfoque

let k;
for (k in obj) {

    /*        k : key
     *   obj[k] : value
     */
}

Utilice este con precaución, ya que podría imprimir propiedades prototipo de obj

✔ El enfoque ES7

for (const [key, value] of Object.entries(obj)) {

}

Sin embargo, en el momento de la edición, no recomendaría el método ES7, porque JavaScript inicializa una gran cantidad de variables internamente para construir este procedimiento (vea los comentarios como prueba). A menos que no esté desarrollando una gran aplicación que merezca optimización, entonces está bien, pero si la optimización es su prioridad, debería pensarlo.

II. Solo necesitamos acceder a cada valor

✔ El enfoque ofyObject.values

let v;
for (v of Object.values(obj)) {

}

Más comentarios sobre las pruebas:

  • El almacenamiento en caché Object.keyso el Object.valuesrendimiento es insignificante

Por ejemplo,

const keys = Object.keys(obj);
let i;
for (i of keys) {
  //
}
// same as
for (i of Object.keys(obj)) {
  //
}
  • Por Object.valuesejemplo, usar un forbucle nativo con variables almacenadas en caché en Firefox parece ser un poco más rápido que usar un for...ofbucle. Sin embargo, la diferencia no es tan importante y Chrome se ejecuta for...ofmás rápido que el forbucle nativo , por lo que recomendaría usarlo for...ofcuando se trate Object.valuesen cualquier caso (4a y 6a prueba).

  • En Firefox, el for...inciclo es realmente lento, por lo que cuando queremos almacenar la clave en caché durante la iteración, es mejor usarla Object.keys. Además, Chrome ejecuta ambas estructuras a la misma velocidad (primera y última prueba).

Puedes consultar las pruebas aquí: https://jsperf.com/es7-and-misc-loops

0
33
for(var k in validation_messages) {
    var o = validation_messages[k];
    do_something_with(o.your_name);
    do_something_else_with(o.your_msg);
}
32

Una versión optimizada y mejorada de la respuesta de AgileJon:

var key, obj, prop, owns = Object.prototype.hasOwnProperty;

for (key in validation_messages ) {

    if (owns.call(validation_messages, key)) {

        obj = validation_messages[key];

        for (prop in obj ) {

            // Using obj.hasOwnProperty might cause you headache if there is
            // obj.hasOwnProperty = function(){return false;}
            // but 'owns' will always work
            if (owns.call(obj, prop)) {
                console.log(prop, "=", obj[prop]);
            }
        }
    }
}
3
  • 2
    ¿Por qué están almacenando hasOwnPropertyen ownsy después de llamar owns.call(obj, prop)en lugar de simplemente llamando obj.hasOwnProperty(prop)como esta respuesta hace? 21/11/12 a las 5:25
  • 15
    Porque objpodría tener la hasOwnPropertyfunción definida en sí misma, por lo que no usará la de Object.prototype. Puede probar antes del forbucle como este obj.hasOwnProperty = function(){return false;}y no iterará sobre ninguna propiedad.
    Azder
    21/11/12 a las 5:38
  • 5
    @Azder +1 por la respuesta y +1 si pudiera por lo bueno de Object.prototype.hasOwnProperty. Lo vi anteriormente dentro del código fuente de la biblioteca de subrayado, pero no sé por qué.
    Samuel
    3 de enero de 2014 a las 15:28
14

p es el valor

for (var key in p) {
  alert(key + ' => ' + p[key]);
}

O

Object.keys(p).forEach(key => { console.log(key, p[key]) })
11

En ES7 puedes hacer:

for (const [key, value] of Object.entries(obj)) {
  //
}
1
  • Hice algunas pruebas, este método es realmente lento cuando se trata de gran cantidad de datos. 24 de septiembre de 2017 a las 3:34
9
for(var key in validation_messages){
    for(var subkey in validation_messages[key]){
        //code here
        //subkey being value, key being 'yourname' / 'yourmsg'
    }
}
0
7

Algunas formas de hacer eso ...

1) Dos capas para ... en bucle ...

for (let key in validation_messages) {
   const vmKeys = validation_messages[key];
   for (let vmKey in vmKeys) {
      console.log(vmKey + vmKeys[vmKey]);
   }
}

2) UtilizandoObject.key

Object.keys(validation_messages).forEach(key => {
   const vmKeys = validation_messages[key];
   Object.keys(vmKeys).forEach(key => {
    console.log(vmKeys + vmKeys[key]);
   });
});

3) función recursiva

const recursiveObj = obj => {
  for(let key in obj){
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      console.log(key + obj[key]);
    } else {
      recursiveObj(obj[key]);
    }
  }
}

Y llámalo como:

recursiveObj(validation_messages);
6

Otra opción:

var testObj = {test: true, test1: false};
for(let x of Object.keys(testObj)){
    console.log(x);
}
1
  • Probé su solución en Chrome 55.0 y aparece un error de tipo. Su respuesta se ve bien y concisa, si puede hacer que funcione, probablemente sería una de las mejores opciones. Traté de resolverlo pero no entiendo tu solución.
    TolMera
    23/12/2016 a las 13:38
5

Aquí viene la versión mejorada y recursiva de la solución de AgileJon ( demo ):

function loopThrough(obj){
  for(var key in obj){
    // skip loop if the property is from prototype
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      //your code
      console.log(key+" = "+obj[key]);
    } else {
      loopThrough(obj[key]);
    }
  }
}
loopThrough(validation_messages);

Esta solución funciona para todo tipo de profundidades diferentes.

4

ECMAScript 2017 , que acaba de finalizar hace un mes, presenta Object.values ​​(). Entonces ahora puedes hacer esto:

let v;
for (v of Object.values(validation_messages))
   console.log(v.your_name);   // jimmy billy
3

Creo que vale la pena señalar que jQuery soluciona esto muy bien con $.each().

Ver: .each ()

Ejemplo:

$('.foo').each(function() {
    console.log($(this));
});

$(this)siendo el único elemento dentro del objeto. Cambie $('.foo')a una variable si no desea utilizar el motor de selección de jQuery.

3

No pude obtener la respuesta anterior para hacer exactamente lo que buscaba.

Después de jugar con las otras respuestas aquí, hice esto. ¡Es hacky, pero funciona!

Para este objeto:

var myObj = {
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"}
};

... este código:

// Get every value in the object into a separate array item ...
function buildArray(p_MainObj, p_Name) {
    var variableList = [];
    var thisVar = "";
    var thisYes = false;
    for (var key in p_MainObj) {
       thisVar = p_Name + "." + key;
       thisYes = false;
       if (p_MainObj.hasOwnProperty(key)) {
          var obj = p_MainObj[key];
          for (var prop in obj) {
            var myregex = /^[0-9]*$/;
            if (myregex.exec(prop) != prop) {
                thisYes = true;
                variableList.push({item:thisVar + "." + prop,value:obj[prop]});
            }
          }
          if ( ! thisYes )
            variableList.push({item:thisVar,value:obj});
       }
    }
    return variableList;
}

// Get the object items into a simple array ...
var objectItems = buildArray(myObj, "myObj");

// Now use them / test them etc... as you need to!
for (var x=0; x < objectItems.length; ++x) {
    console.log(objectItems[x].item + " = " + objectItems[x].value);
}

... produce esto en la consola:

myObj.pageURL = BLAH
myObj.emailBox.model = emailAddress
myObj.emailBox.selector = #emailAddress
myObj.passwordBox.model = password
myObj.passwordBox.selector = #password
3

var obj = {
    name: "SanD",
    age: "27"
}
Object.keys(obj).forEach((key) => console.log(key,obj[key]));

Para recorrer el Objeto JavaScript podemos usar forEach y para optimizar el código podemos usar la función de flecha.

0

Usar ES8 Object.entries () debería ser una forma más compacta de lograr esto.

Object.entries(validation_messages).map(([key,object]) => {

    alert(`Looping through key : ${key}`);

    Object.entries(object).map(([token, value]) => {
        alert(`${token} : ${value}`);
    });
});
0
0

La solución que me funciona es la siguiente:

_private.convertParams = function(params){
    var params = [];
    Object.keys(values).forEach(function(key) {
        params.push({"id":key, "option":"Igual", "value":params[key].id})
    });
    return params;
}
0

Exótico - travesía profunda

JSON.stringify(validation_messages,(field,value)=>{
  if(!field) return value;

  // ... your code

  return value;
})

En esta solución utilizamos el reemplazador que permite recorrer en profundidad todo el objeto y los objetos anidados; en cada nivel obtendrá todos los campos y valores. Si necesita obtener la ruta completa a cada campo, mire aquí .

var validation_messages = {
    "key_1": {
        "your_name": "jimmy",
        "your_msg": "hello world"
    },
    "key_2": {
        "your_name": "billy",
        "your_msg": "foo equals bar",
        "deep": {
          "color": "red",
          "size": "10px"
        }
    }
}

JSON.stringify(validation_messages,(field,value)=>{
  if(!field) return value;

  console.log(`key: ${field.padEnd(11)} - value: ${value}`);

  return value;
})
0

En 2020 quieres funciones inmutables y universales

Esto recorre su objeto multidimensional compuesto de subobjetos, matrices y cadenas y aplica una función personalizada:

export const iterate = (object, func) => {
  const entries = Object.entries(object).map(([key, value]) =>
    Array.isArray(value)
      ? [key, value.map(e => iterate(e, func))]
      : typeof value === 'object'
      ? [key, iterate(value, func)]
      : [key, func(value)]
  );
  return Object.fromEntries(entries);
};

Uso:

const r = iterate(data, e=>'converted_'+e);
console.log(r);
0

forEach2

(Encontrado aquí ):

var lunch = {
    sandwich: 'ham',
    age: 48,
};
lunch.forEach2(function (item, key) {
    console.log(key);
    console.log(item);
});

Código:

if (!Object.prototype.forEach2) {
    Object.defineProperty(Object.prototype, 'forEach2', {
        value: function (callback, thisArg) {
            if (this == null) {
                throw new TypeError('Not an object');
            }
            thisArg = thisArg || window;
            for (var key in this) {
                if (this.hasOwnProperty(key)) {
                    callback.call(thisArg, this[key], key, this);
                }
            }
        }
    });
}
0
var validation_messages = {
    "key_1": {
        "your_name": "jimmy",
        "your_msg": "hello world"
    },
    "key_2": {
        "your_name": "billy",
        "your_msg": "foo equals bar"
    }
}
for (var i in validation_messages) {
    console.log("i = \"" + i + "\"");
    console.log("validation_messages[\"" + i + "\"] = ");
    console.log(validation_messages[i]);
    console.log("\n");
    for (var j in validation_messages[i]) {
        console.log("j = \"" + j + "\"");
        console.log("validation_messages[\"" + i + "\"][\"" + j + "\"] = \"" + validation_messages[i][j] + "\"");
        console.log("\n");
    }
    console.log('\n');
}

Salidas:

i = "key_1"
validation_messages["key_1"] = 
{
  your_name:"jimmy",
  your_msg:"hello world"
}

j = "your_name"
validation_messages["key_1"]["your_name"] = "jimmy"

j = "your_msg"
validation_messages["key_1"]["your_msg"] = "hello world"


i = "key_2"
validation_messages["key_2"] = 
{
  your_name:"billy",
  your_msg:"foo equals bar"
}

j = "your_name"
validation_messages["key_2"]["your_name"] = "billy"

j = "your_msg"
validation_messages["key_2"]["your_msg"] = "foo equals bar"
0
-7

En mi caso (sobre la base de lo anterior) es posible para cualquier número de niveles.

var myObj = {
    rrr: undefined,
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"},
    proba: {odin:{dva:"rr",trr:"tyuuu"}, od:{ff:5,ppa:{ooo:{lll:'lll'}},tyt:'12345'}}
};


function lookdeep(obj,p_Name,gg){
    var A=[], tem, wrem=[], dd=gg?wrem:A;
    for(var p in obj){
        var y1=gg?'':p_Name, y1=y1 + '.' + p;
        if(obj.hasOwnProperty(p)){
           var tem=obj[p];
           if(tem && typeof tem=='object'){
               a1=arguments.callee(tem,p_Name,true);
               if(a1 && typeof a1=='object'){for(i in a1){dd.push(y1 + a1[i])};}
            }
            else{
               dd.push(y1 + ':' + String(tem));
            }
        }
    };
    return dd
};


var s=lookdeep(myObj,'myObj',false);
for (var x=0; x < s.length; ++x) {
console.log(s[x]+'\n');}

Resultado:

["myObj.rrr:undefined",
"myObj.pageURL:BLAH",
"myObj.emailBox.model:emailAddress",
"myObj.emailBox.selector:#emailAddress",
"myObj.passwordBox.model:password",
"myObj.passwordBox.selector:#password",
"myObj.proba.odin.dva:rr",
"myObj.proba.odin.trr:tyuuu",
"myObj.proba.od.ff:5",
"myObj.proba.od.ppa.ooo.lll:lll",
"myObj.proba.od.tyt:12345"]
0