Tengo una estructura de datos anidada que contiene objetos y matrices. ¿Cómo puedo extraer la información, es decir, acceder a valores específicos o múltiples (o claves)?
Por ejemplo:
var data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
¿Cómo puedo acceder name
al segundo elemento en items
?
javascript
arrays
object
nested
data-manipulation
Felix Kling
fuente
fuente
Respuestas:
Preliminares
JavaScript tiene un solo tipo de datos que puede contener múltiples valores: Objeto . Una matriz es una forma especial de objeto.
(Normal) Los objetos tienen la forma
Las matrices tienen la forma
Tanto las matrices como los objetos exponen una
key -> value
estructura. Las claves en una matriz deben ser numéricas, mientras que cualquier cadena se puede usar como clave en los objetos. Los pares clave-valor también se denominan "propiedades" .Se puede acceder a las propiedades utilizando la notación de puntos
o notación de paréntesis , si el nombre de la propiedad no sería un nombre de identificador de JavaScript válido [especificación] , o si el nombre es el valor de una variable:
Por esa razón, solo se puede acceder a los elementos de la matriz mediante la notación de paréntesis:
Espera ... ¿qué hay de JSON?
JSON es una representación textual de datos, al igual que XML, YAML, CSV y otros. Para trabajar con dichos datos, primero debe convertirse a tipos de datos de JavaScript, es decir, matrices y objetos (y cómo se explicó cómo trabajar con ellos). ¿Cómo analizar JSON se explica en la pregunta Parse JSON en JavaScript? .
Material de lectura adicional
Cómo acceder a matrices y objetos es un conocimiento fundamental de JavaScript y, por lo tanto, es recomendable leer la Guía de JavaScript de MDN , especialmente las secciones
Acceder a estructuras de datos anidados
Una estructura de datos anidados es una matriz u objeto que se refiere a otras matrices u objetos, es decir, sus valores son matrices u objetos. Se puede acceder a dichas estructuras aplicando consecutivamente la notación de punto o paréntesis.
Aquí hay un ejemplo:
Supongamos que queremos acceder
name
al segundo elemento.Así es como podemos hacerlo paso a paso:
Como podemos ver
data
es un objeto, por lo tanto, podemos acceder a sus propiedades utilizando la notación de puntos. Seitems
accede a la propiedad de la siguiente manera:El valor es una matriz, para acceder a su segundo elemento, tenemos que usar la notación de paréntesis:
Este valor es un objeto y usamos la notación de puntos nuevamente para acceder a la
name
propiedad. Entonces eventualmente obtenemos:Alternativamente, podríamos haber usado la notación de corchetes para cualquiera de las propiedades, especialmente si el nombre contenía caracteres que lo hubieran hecho inválido para el uso de la notación de puntos:
Estoy tratando de acceder a una propiedad pero solo obtengo la
undefined
devolución.La mayoría de las veces cuando obtienes
undefined
, el objeto / matriz simplemente no tiene una propiedad con ese nombre.Use
console.log
oconsole.dir
e inspeccione la estructura del objeto / matriz. La propiedad a la que está intentando acceder podría estar definida en un objeto / matriz anidado.¿Qué sucede si los nombres de las propiedades son dinámicos y no los conozco de antemano?
Si los nombres de las propiedades son desconocidos o si queremos acceder a todas las propiedades de un objeto / elementos de una matriz, podemos usar el bucle
for...in
[MDN] para los objetos y elfor
[MDN] bucle para que las matrices iteren sobre todas las propiedades / elementos.Objetos
Para iterar sobre todas las propiedades de
data
, podemos iterar sobre el objeto así:Dependiendo de dónde provenga el objeto (y qué desea hacer), es posible que deba probar en cada iteración si la propiedad es realmente una propiedad del objeto o si es una propiedad heredada. Puede hacer esto con
Object#hasOwnProperty
[MDN] .Como alternativa a
for...in
withhasOwnProperty
, puede usarObject.keys
[MDN] para obtener una matriz de nombres de propiedades :Matrices
Para iterar sobre todos los elementos de la
data.items
matriz , usamos unfor
bucle:También se podría usar
for...in
para iterar sobre las matrices, pero hay razones por las cuales esto debe evitarse: ¿por qué es 'for (elemento var en la lista)' con las matrices consideradas una mala práctica en JavaScript? .Con la creciente compatibilidad del navegador de ECMAScript 5, el método de matriz
forEach
[MDN] también se convierte en una alternativa interesante:En entornos compatibles con ES2015 (ES6), también puede usar el bucle [MDN] , que no solo funciona para matrices, sino para cualquier iterable :
for...of
En cada iteración,
for...of
directamente nos da el siguiente elemento de la iterable, no hay "índice" para acceder o usar.¿Qué pasa si la "profundidad" de la estructura de datos es desconocida para mí?
Además de las claves desconocidas, la "profundidad" de la estructura de datos (es decir, cuántos objetos anidados) tiene, también puede ser desconocida. La forma de acceder a propiedades profundamente anidadas generalmente depende de la estructura de datos exacta.
Pero si la estructura de datos contiene patrones repetitivos, por ejemplo, la representación de un árbol binario, la solución generalmente incluye acceder recursivamente [Wikipedia] a cada nivel de la estructura de datos.
Aquí hay un ejemplo para obtener el primer nodo hoja de un árbol binario:
Mostrar fragmento de código
Una forma más genérica de acceder a una estructura de datos anidados con claves y profundidad desconocidas es probar el tipo del valor y actuar en consecuencia.
Aquí hay un ejemplo que agrega todos los valores primitivos dentro de una estructura de datos anidados en una matriz (suponiendo que no contenga ninguna función). Si nos encontramos con un objeto (o matriz) simplemente llamamos
toArray
nuevamente a ese valor (llamada recursiva).Mostrar fragmento de código
Ayudantes
Dado que la estructura de un objeto complejo o matriz no es necesariamente obvia, podemos inspeccionar el valor en cada paso para decidir cómo avanzar.
console.log
[MDN] yconsole.dir
[MDN] nos ayudan a hacer esto. Por ejemplo (salida de la consola de Chrome):Aquí vemos que
data.items
es una matriz con dos elementos que son ambos objetos. En la consola de Chrome, los objetos pueden incluso expandirse e inspeccionarse de inmediato.Esto nos dice que
data.items[1]
es un objeto, y después de expandirlo vemos que tiene tres propiedadesid
,name
y__proto__
. Esta última es una propiedad interna utilizada para la cadena prototipo del objeto. Sin embargo, la cadena de prototipos y la herencia están fuera del alcance de esta respuesta.fuente
let object = {a: 1, b: 2, c: { a: 3, b: 4 }};
, esto devuelve una matriz que contiene una matriz para cada objeto anidado, en este caso[ 1, 2, [ 3, 4 ] ]
¿No sería mejor usar concat en la llamada recursiva en lugar de push? (requiere que el resultado sea mutable)Puedes acceder de esta manera
o
Ambas formas son iguales.
fuente
En caso de que intente acceder a una
item
estructura de ejemplo medianteid
oname
, sin conocer su posición en la matriz, la forma más fácil de hacerlo sería utilizar la biblioteca underscore.js :Según mi experiencia, el uso de funciones de orden superior en lugar de
for
ofor..in
bucles da como resultado un código que es más fácil de razonar y, por lo tanto, más fácil de mantener.Solo mis 2 centavos.
fuente
Los objetos y las matrices tienen muchos métodos integrados que pueden ayudarlo con el procesamiento de datos.
Nota: en muchos de los ejemplos estoy usando funciones de flecha . Son similares a las expresiones de función , pero unen el
this
valor léxicamente.Object.keys()
,Object.values()
(ES 2017) yObject.entries()
(ES 2017)Object.keys()
devuelve una matriz de claves de objeto,Object.values()
devuelve una matriz de valores de objeto yObject.entries()
devuelve una matriz de claves de objeto y los valores correspondientes en un formato[key, value]
.Object.entries()
con una asignación de for-of loop y desestructuraciónEs muy conveniente iterar el resultado
Object.entries()
con un bucle for-of y una asignación de desestructuración .For-of loop le permite iterar elementos de la matriz. La sintaxis es
for (const element of array)
(podemos reemplazarconst
convar
olet
, pero es mejor usarlaconst
si no pretendemos modificarlaelement
).La asignación de estructura le permite extraer valores de una matriz o un objeto y asignarlos a variables. En este caso,
const [key, value]
significa que en lugar de asignar la[key, value]
matriz aelement
, asignamos el primer elemento de esa matriz akey
y el segundo elemento avalue
. Es equivalente a esto:Como puede ver, la desestructuración hace que esto sea mucho más simple.
Array.prototype.every()
yArray.prototype.some()
El
every()
método se devuelvetrue
si la función de devolución de llamada especificada se devuelvetrue
para cada elemento de la matriz. Elsome()
método se devuelvetrue
si la función de devolución de llamada especificada se devuelvetrue
para algún elemento (al menos uno).Array.prototype.find()
yArray.prototype.filter()
Los
find()
métodos devuelven el primer elemento que satisface la función de devolución de llamada proporcionada. Elfilter()
método devuelve una matriz de todos los elementos que satisface la función de devolución de llamada proporcionada.Array.prototype.map()
El
map()
método devuelve una matriz con los resultados de llamar a una función de devolución de llamada proporcionada en los elementos de la matriz.Array.prototype.reduce()
El
reduce()
método reduce una matriz a un solo valor llamando a la función de devolución de llamada proporcionada con dos elementos.El
reduce()
método toma un segundo parámetro opcional, que es el valor inicial. Esto es útil cuando la matriz en la que llamareduce()
puede tener cero o uno elementos. Por ejemplo, si quisiéramos crear una funciónsum()
que tome una matriz como argumento y devuelva la suma de todos los elementos, podríamos escribirla así:fuente
Object.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
A veces, puede ser conveniente acceder a un objeto anidado utilizando una cadena. El enfoque simple es el primer nivel, por ejemplo
Pero este no suele ser el caso con el complejo json. A medida que json se vuelve más complejo, los enfoques para encontrar valores dentro de json también se vuelven complejos. Un enfoque recursivo para navegar por el json es el mejor, y cómo se aprovecha esa recursión dependerá del tipo de datos que se busque. Si hay declaraciones condicionales involucradas, una búsqueda json puede ser una buena herramienta para usar.
Si la propiedad a la que se accede ya es conocida, pero la ruta es compleja, por ejemplo, en este objeto
Y sabe que desea obtener el primer resultado de la matriz en el objeto, tal vez le gustaría usar
Sin embargo, eso causará una excepción ya que no hay propiedad de objeto con ese nombre. La solución para poder usar esto sería aplanar el aspecto del árbol del objeto. Esto se puede hacer de forma recursiva.
Ahora, el objeto complejo puede ser aplanado
Aquí se
jsFiddle Demo
está utilizando uno de estos enfoques.fuente
obj["arr[0].name"]
lugar deobj.arr[0].name
? Apenas necesita / desea lidiar con objetos aplanados, excepto para la serialización.Esta pregunta es bastante antigua, como una actualización contemporánea. Con el inicio de ES2015, existen alternativas para obtener los datos que necesita. Ahora hay una característica llamada desestructuración de objetos para acceder a objetos anidados.
El ejemplo anterior crea una variable llamada
secondName
desde laname
clave desde una matriz llamadaitems
, el solitario,
dice omitir el primer objeto en la matriz.En particular, es probable que sea excesivo para este ejemplo, ya que el acceso simple a la matriz es más fácil de leer, pero resulta útil al separar objetos en general.
Esta es una introducción muy breve a su caso de uso específico, la desestructuración puede ser una sintaxis inusual para acostumbrarse al principio. Recomiendo leer la documentación de Asignación de Desestructuración de Mozilla para obtener más información.
fuente
Para acceder a un atributo anidado, debe especificar su nombre y luego buscar a través del objeto.
Si ya conoce la ruta exacta, puede codificarla en su script de la siguiente manera:
estos también funcionan
Cuando no conoce el nombre exacto de antemano, o un usuario es quien le proporciona el nombre. Luego se requiere una búsqueda dinámica a través de la estructura de datos. Algunos sugirieron aquí que la búsqueda se puede hacer usando un
for
bucle, pero hay una manera muy simple de recorrer una ruta usandoArray.reduce
.La ruta es una forma de decir: Primero tome el objeto con clave
items
, que resulta ser una matriz. Luego tome el1
elemento -st (0 arrays de índice). Por último, tome el objeto con clavename
en ese elemento de matriz, que resulta ser la cadenabar
.Si tiene un camino muy largo, incluso podría utilizarlo
String.split
para facilitar todo esto:Esto es simplemente JavaScript, sin usar bibliotecas de terceros como jQuery o lodash.
fuente
o
Básicamente, use un punto entre cada descendiente que se despliegue debajo de él y cuando tenga nombres de objetos hechos de dos cadenas, debe usar la notación ["obj Name"]. De lo contrario, solo un punto sería suficiente;
Fuente: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects
para agregar a esto, el acceso a las matrices anidadas sucedería así:
Fuente: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-arrays/
Otro documento más útil que describe la situación anterior: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation
Acceso a la propiedad a través de dot walking: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation
fuente
Puedes usar la
lodash _get
función:fuente
Usar JSONPath sería una de las soluciones más flexibles si está dispuesto a incluir una biblioteca: https://github.com/s3u/JSONPath (nodo y navegador)
Para su caso de uso, la ruta json sería:
entonces:
fuente
Por si acaso, cualquiera está visitando esta pregunta en 2017 o más tarde y está buscando una forma fácil de recordar , aquí hay una publicación de blog elaborada sobre Acceso a objetos anidados en JavaScript sin ser engañado por
No se puede leer la propiedad 'foo' de undefined error
1. Patrón de acceso a objetos anidados de Oliver Steele
La forma más fácil y limpia es usar el patrón de acceso a objetos anidados de Oliver Steele
Con esta notación, nunca te encontrarás
No se puede leer la propiedad 'nombre' de undefined .
Básicamente verifica si el usuario existe, si no, crea un objeto vacío sobre la marcha. De esta forma, siempre se accederá a la clave de nivel siguiente desde un objeto que existe o un objeto vacío , pero nunca desde indefinido.
2. Acceda a objetos anidados utilizando la reducción de matriz
Para poder acceder a matrices anidadas, puede escribir su propia matriz para reducir la utilidad.
También hay un tipo excelente que maneja el tipo de biblioteca mínimo que hace todo esto por usted.
fuente
((user || {}).address || new Array(3))[1].name
...[1].bar
se produciría un error si el elemento1
no existiera. Pero ese también es el caso de....foo.bar
sifoo
no existiera. También debe "proteger" el acceso1
, al igual que "protege" cualquier otro acceso a la propiedad. Una matriz es solo un objeto. Un "elemento de matriz" es solo una propiedad. Correctamente aplicado sería(((user || {}).address || {})[1] || {}).name
.Prefiero JQuery. Es más limpio y fácil de leer.
fuente
Acceder dinámicamente a objetos de múltiples niveles.
Violín de trabajo: https://jsfiddle.net/andreitodorut/3mws3kjL/
fuente
Si está buscando uno o más objetos que cumplan ciertos criterios, tiene algunas opciones con query-js
También hay a
single
y asingleOrDefault
, funcionan de manera similarfirst
yfirstOrDefault
respectivamente. La única diferencia es que lanzarán si se encuentra más de una coincidencia.Para una explicación más detallada de query-js, puede comenzar con esta publicación
fuente
La forma de subrayar js
Que es una biblioteca de JavaScript que proporciona un montón de
functional programming
ayudantes útiles sin extender ningún objeto incorporado.Solución:
fuente
Antigua pregunta, pero como nadie mencionó lodash (solo subrayado).
En caso de que ya esté usando lodash en su proyecto, creo que es una forma elegante de hacerlo en un ejemplo complejo:
Opt 1
igual que:
Opt 2
La diferencia entre la primera y la segunda opción es que en Opt 1 si le falta una de las propiedades (indefinida) en la ruta, no obtiene un error, le devuelve el tercer parámetro.
Para el filtro de matriz lodash tiene,
_.find()
pero prefiero usar el regularfilter()
. Pero sigo pensando que el método anterior_.get()
es súper útil cuando se trabaja con datos realmente complejos. En el pasado me enfrenté a API realmente complejas y ¡fue útil!Espero que pueda ser útil para quién está buscando opciones para manipular datos realmente complejos que implica el título.
fuente
No creo que el interrogador solo se refiera a un objeto anidado de un nivel, así que presento la siguiente demostración para demostrar cómo acceder al nodo del objeto json profundamente anidado. Muy bien, busquemos el nodo con id '5'.
fuente
Puede usar la sintaxis
jsonObject.key
para acceder al valor. Y si desea acceder a un valor desde una matriz, puede usar la sintaxisjsonObjectArray[index].key
.Aquí están los ejemplos de código para acceder a varios valores para darle la idea.
fuente
Enfoque dinámico
En la
deep(data,key)
función a continuación , puede usar unakey
cadena arbitraria , en su casoitems[1].name
(puede usar la notación de matriz[i]
en cualquier nivel), si la clave no es válida, se devuelve indefinido.Mostrar fragmento de código
fuente
Un enfoque pitónico, recursivo y funcional para desentrañar árboles JSON arbitrarios:
donde los datos son una lista de python (analizada a partir de una cadena de texto JSON):
fuente
La función grep de jQuery le permite filtrar a través de una matriz:
fuente
fuente
En 2020, puede usar @ babel / plugin-offer-optional-chaining, es muy fácil acceder a valores anidados en un objeto.
https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
https://github.com/tc39/proposal-optional-chaining
fuente
Mi
stringdata
proviene de un archivo PHP, pero aún así, indico aquí envar
. Cuando tomo directamente mi jsonobj
, no se mostrará nada, por eso pongo mi archivo json comovar obj=JSON.parse(stringdata);
así que después de eso obtengomessage
obj y muestro en el cuadro de alerta, obtengodata
cuál es la matriz json y la almaceno en una variable,ArrObj
luego leo el primer objeto de esa matriz con un valor clave como esteArrObj[0].id
fuente
stringjson
no es una cadena.Usar lodash sería una buena solución
Ex:
fuente