Recorrer recursivamente un objeto para crear una lista de propiedades

Resuelto Jake asked hace 11 años • 20 respuestas

Situación: Tengo un objeto grande que contiene múltiples subobjetos y subsubobjetos, con propiedades que contienen múltiples tipos de datos. Para nuestros propósitos, este objeto se parece a esto:

var object = {
    aProperty: {
        aSetting1: 1,
        aSetting2: 2,
        aSetting3: 3,
        aSetting4: 4,
        aSetting5: 5
    },
    bProperty: {
        bSetting1: {
            bPropertySubSetting : true
        },
        bSetting2: "bString"
    },
    cProperty: {
        cSetting: "cString"
    }
}

Necesito recorrer este objeto y crear una lista de claves que muestre la jerarquía, para que la lista termine luciendo así:

aProperty.aSetting1
aProperty.aSetting2
aProperty.aSetting3
aProperty.aSetting4
aProperty.aSetting5
bProperty.bSetting1.bPropertySubSetting
bProperty.bSetting2
cProperty.cSetting

Tengo esta función, que recorre el objeto y escupe las claves, pero no de forma jerárquica:

function iterate(obj) {
    for (var property in obj) {
        if (obj.hasOwnProperty(property)) {
            if (typeof obj[property] == "object") {
                iterate(obj[property]);
            }
            else {
                console.log(property + "   " + obj[property]);
            }
        }
    }
}

¿Alguien puede decirme cómo hacer esto? Aquí tienes un jsfiddle con el que puedes jugar: http://jsfiddle.net/tbynA/

Jake avatar Mar 29 '13 02:03 Jake
Aceptado

Hice un VIOLÍN para ti. Estoy almacenando una stackcadena y luego la envío, si la propiedad es de tipo primitivo:

function iterate(obj, stack) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                    iterate(obj[property], stack + '.' + property);
                } else {
                    console.log(property + "   " + obj[property]);
                    $('#output').append($("<div/>").text(stack + '.' + property))
                }
            }
        }
    }

iterate(object, '')

Actualizar:17/01/2019

Solía ​​​​haber una implementación diferente, pero no funcionó. Vea esta respuesta para una solución más bonita.

Artyom Neustroev avatar Mar 28 '2013 19:03 Artyom Neustroev

La solución de Artyom Neustroev no funciona en objetos complejos, así que aquí hay una solución funcional basada en su idea:

function propertiesToArray(obj) {
  const isObject = val =>
    val && typeof val === 'object' && !Array.isArray(val);

  const addDelimiter = (a, b) =>
    a ? `${a}.${b}` : b;

  const paths = (obj = {}, head = '') => {
    return Object.entries(obj)
      .reduce((product, [key, value]) => 
        {
          let fullPath = addDelimiter(head, key)
          return isObject(value) ?
            product.concat(paths(value, fullPath))
          : product.concat(fullPath)
        }, []);
  }

  return paths(obj);
}
  
const foo = {foo: {bar: {baz: undefined}, fub: 'goz', bag: {zar: {zaz: null}, raz: 3}}}
const result = propertiesToArray(foo)
console.log(result)
Expandir fragmento

EDITAR (23/05/2023):

Hay 4 soluciones diferentes (completas) con descripciones completas disponibles en LeetCode: https://leetcode.com/problems/array-of-objects-to-matrix/editorial/?utm_campaign=PostD19&utm_medium=Post&utm_source=Post&gio_link_id=EoZk0Zy9

Matjaz avatar Dec 04 '2018 20:12 Matjaz

Tendrás problemas con esto si el objeto tiene un bucle en su gráfico de objetos, por ejemplo, algo como:

var object = {
    aProperty: {
        aSetting1: 1
    },
};
object.ref = object;

En ese caso, es posible que desee conservar las referencias de los objetos que ya ha recorrido y excluirlos de la iteración.

También puedes encontrarte con un problema si el gráfico de objetos es demasiado profundo, como:

var object = {
  a: { b: { c: { ... }} }
};

Obtendrás un error de demasiadas llamadas recursivas. Ambos se pueden evitar:

function iterate(obj) {
    var walked = [];
    var stack = [{obj: obj, stack: ''}];
    while(stack.length > 0)
    {
        var item = stack.pop();
        var obj = item.obj;
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                  var alreadyFound = false;
                  for(var i = 0; i < walked.length; i++)
                  {
                    if (walked[i] === obj[property])
                    {
                      alreadyFound = true;
                      break;
                    }
                  }
                  if (!alreadyFound)
                  {
                    walked.push(obj[property]);
                    stack.push({obj: obj[property], stack: item.stack + '.' + property});
                  }
                }
                else
                {
                    console.log(item.stack + '.' + property + "=" + obj[property]);
                }
            }
        }
    }
}

iterate(object); 
Ondrej Svejdar avatar Feb 11 '2016 15:02 Ondrej Svejdar

https://github.com/hughsk/flat

var flatten = require('flat')
flatten({
key1: {
    keyA: 'valueI'
},
key2: {
    keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})

// {
//   'key1.keyA': 'valueI',
//   'key2.keyB': 'valueII',
//   'key3.a.b.c': 2
// }

Simplemente haga un bucle para obtener los índices después.

Matthieu Drula avatar Apr 17 '2016 17:04 Matthieu Drula