¿Cómo puedo acceder y procesar objetos anidados, matrices o JSON?

875

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 nameal segundo elemento en items?

Felix Kling
fuente
22
@Marcel: Tiene que leerse como "Tengo una estructura de datos anidada de datos o JSON, ¿cómo puedo acceder a un valor específico?". Yo sé la diferencia, pero muchas personas no, y no podría estar buscando "JSON" en lugar de "objeto". Muchas preguntas en realidad son de la forma "¿Cómo puedo acceder a X en este JSON". El único lugar donde menciono JSON en mi respuesta es donde explico de qué se trata. Si tiene alguna sugerencia sobre cómo comunicar esto de una mejor manera, soy todo oídos.
Felix Kling
posible duplicado de JSON encontrar en JavaScript
Travis J

Respuestas:

1160

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

{key: value, key: value, ...}

Las matrices tienen la forma

[value, value, ...]

Tanto las matrices como los objetos exponen una key -> valueestructura. 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

const value = obj.someProperty;

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:

// the space is not a valid character in identifier names
const value = obj["some Property"];

// property name as variable
const name = "some Property";
const value = obj[name];

Por esa razón, solo se puede acceder a los elementos de la matriz mediante la notación de paréntesis:

const value = arr[5]; // arr.5 would be a syntax error

// property name / index as variable
const x = 5;
const value = arr[x];

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:

const data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

Supongamos que queremos acceder nameal segundo elemento.

Así es como podemos hacerlo paso a paso:

Como podemos ver dataes un objeto, por lo tanto, podemos acceder a sus propiedades utilizando la notación de puntos. Se itemsaccede a la propiedad de la siguiente manera:

data.items

El valor es una matriz, para acceder a su segundo elemento, tenemos que usar la notación de paréntesis:

data.items[1]

Este valor es un objeto y usamos la notación de puntos nuevamente para acceder a la namepropiedad. Entonces eventualmente obtenemos:

const item_name = data.items[1].name;

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:

const item_name = data['items'][1]['name'];

Estoy tratando de acceder a una propiedad pero solo obtengo la undefineddevolución.

La mayoría de las veces cuando obtienes undefined, el objeto / matriz simplemente no tiene una propiedad con ese nombre.

const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined

Use console.logo console.dire inspeccione la estructura del objeto / matriz. La propiedad a la que está intentando acceder podría estar definida en un objeto / matriz anidado.

console.log(foo.bar.baz); // 42

¿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 el for [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í:

for (const prop in data) {
    // `prop` contains the name of each property, i.e. `'code'` or `'items'`
    // consequently, `data[prop]` refers to the value of each property, i.e.
    // either `42` or the array
}

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...inwith hasOwnProperty, puede usar Object.keys [MDN] para obtener una matriz de nombres de propiedades :

Object.keys(data).forEach(function(prop) {
  // `prop` is the property name
  // `data[prop]` is the property value
});

Matrices

Para iterar sobre todos los elementos de la data.items matriz , usamos un forbucle:

for(let i = 0, l = data.items.length; i < l; i++) {
    // `i` will take on the values `0`, `1`, `2`,..., i.e. in each iteration
    // we can access the next element in the array with `data.items[i]`, example:
    // 
    // var obj = data.items[i];
    // 
    // Since each element is an object (in our example),
    // we can now access the objects properties with `obj.id` and `obj.name`. 
    // We could also use `data.items[i].id`.
}

También se podría usar for...inpara 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:

data.items.forEach(function(value, index, array) {
    // The callback is executed for each element in the array.
    // `value` is the element itself (equivalent to `array[index]`)
    // `index` will be the index of the element in the array
    // `array` is a reference to the array itself (i.e. `data.items` in this case)
}); 

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

for (const item of data.items) {
   // `item` is the array element, **not** the index
}

En cada iteración, for...ofdirectamente 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:

function getLeaf(node) {
    if (node.leftChild) {
        return getLeaf(node.leftChild); // <- recursive call
    }
    else if (node.rightChild) {
        return getLeaf(node.rightChild); // <- recursive call
    }
    else { // node must be a leaf node
        return node;
    }
}

const first_leaf = getLeaf(root);

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 toArraynuevamente a ese valor (llamada recursiva).

function toArray(obj) {
    const result = [];
    for (const prop in obj) {
        const value = obj[prop];
        if (typeof value === 'object') {
            result.push(toArray(value)); // <- recursive call
        }
        else {
            result.push(value);
        }
    }
    return result;
}



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] y console.dir [MDN] nos ayudan a hacer esto. Por ejemplo (salida de la consola de Chrome):

> console.log(data.items)
 [ Object, Object ]

Aquí vemos que data.itemses una matriz con dos elementos que son ambos objetos. En la consola de Chrome, los objetos pueden incluso expandirse e inspeccionarse de inmediato.

> console.log(data.items[1])
  Object
     id: 2
     name: "bar"
     __proto__: Object

Esto nos dice que data.items[1]es un objeto, y después de expandirlo vemos que tiene tres propiedades id, namey __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.

Felix Kling
fuente
3
Algo de lo que se vincula aquí realmente está preguntando cómo hacer esto en jQuery, lo que, para ser justos, simplifica 1 o 2 cosas aquí. No estoy seguro de si hacer que esto sea más como un megapost o responderlas por separado: los conceptos básicos cubiertos aquí sobre qué es un objeto, qué es una matriz, generalmente son lo que realmente se pregunta ...
Chris Moschini
1
@ felix-kling Una cosa ... con objetos anidados, como 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)
ElFitz
3
Esta es la respuesta más profunda que he visto en Stack Overflow, ¡y respondió mi pregunta! ¡Gracias!
William Jones
Esta página me hizo aprender la diferencia entre ARRAY y OBJ
4ni5
76

Puedes acceder de esta manera

data.items[1].name

o

data["items"][1]["name"]

Ambas formas son iguales.

vitmalina
fuente
Sí, pero no puede hacer datos ["elementos"]. 1.name
neaumusic
55
Primero es mucho más intuitivo, legible y más corto;) Prefiero usar la sintaxis de propiedad de corchete solo cuando el nombre de la propiedad es variable.
DanteTheSmith
35

En caso de que intente acceder a una itemestructura de ejemplo mediante ido name, sin conocer su posición en la matriz, la forma más fácil de hacerlo sería utilizar la biblioteca underscore.js :

var data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

_.find(data.items, function(item) {
  return item.id === 2;
});
// Object {id: 2, name: "bar"}

Según mi experiencia, el uso de funciones de orden superior en lugar de foro for..inbucles 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.

principio holográfico
fuente
29

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 thisvalor léxicamente.

Object.keys(), Object.values()(ES 2017) y Object.entries()(ES 2017)

Object.keys()devuelve una matriz de claves de objeto, Object.values()devuelve una matriz de valores de objeto y Object.entries()devuelve una matriz de claves de objeto y los valores correspondientes en un formato [key, value].

const obj = {
  a: 1
 ,b: 2
 ,c: 3
}

console.log(Object.keys(obj)) // ['a', 'b', 'c']
console.log(Object.values(obj)) // [1, 2, 3]
console.log(Object.entries(obj)) // [['a', 1], ['b', 2], ['c', 3]]

Object.entries() con una asignación de for-of loop y desestructuración

const obj = {
  a: 1
 ,b: 2
 ,c: 3
}

for (const [key, value] of Object.entries(obj)) {
  console.log(`key: ${key}, value: ${value}`)
}

Es 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 reemplazar constcon varo let, pero es mejor usarla constsi no pretendemos modificarla element).

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 a element, asignamos el primer elemento de esa matriz a keyy el segundo elemento a value. Es equivalente a esto:

for (const element of Object.entries(obj)) {
  const key = element[0]
       ,value = element[1]
}

Como puede ver, la desestructuración hace que esto sea mucho más simple.

Array.prototype.every() y Array.prototype.some()

El every()método se devuelve truesi la función de devolución de llamada especificada se devuelve truepara cada elemento de la matriz. El some()método se devuelve truesi la función de devolución de llamada especificada se devuelve truepara algún elemento (al menos uno).

const arr = [1, 2, 3]

// true, because every element is greater than 0
console.log(arr.every(x => x > 0))
// false, because 3^2 is greater than 5
console.log(arr.every(x => Math.pow(x, 2) < 5))
// true, because 2 is even (the remainder from dividing by 2 is 0)
console.log(arr.some(x => x % 2 === 0))
// false, because none of the elements is equal to 5
console.log(arr.some(x => x === 5))

Array.prototype.find() y Array.prototype.filter()

Los find()métodos devuelven el primer elemento que satisface la función de devolución de llamada proporcionada. El filter()método devuelve una matriz de todos los elementos que satisface la función de devolución de llamada proporcionada.

const arr = [1, 2, 3]

// 2, because 2^2 !== 2
console.log(arr.find(x => x !== Math.pow(x, 2)))
// 1, because it's the first element
console.log(arr.find(x => true))
// undefined, because none of the elements equals 7
console.log(arr.find(x => x === 7))

// [2, 3], because these elements are greater than 1
console.log(arr.filter(x => x > 1))
// [1, 2, 3], because the function returns true for all elements
console.log(arr.filter(x => true))
// [], because none of the elements equals neither 6 nor 7
console.log(arr.filter(x => x === 6 || x === 7))

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.

const arr = [1, 2, 3]

console.log(arr.map(x => x + 1)) // [2, 3, 4]
console.log(arr.map(x => String.fromCharCode(96 + x))) // ['a', 'b', 'c']
console.log(arr.map(x => x)) // [1, 2, 3] (no-op)
console.log(arr.map(x => Math.pow(x, 2))) // [1, 4, 9]
console.log(arr.map(String)) // ['1', '2', '3']

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.

const arr = [1, 2, 3]

// Sum of array elements.
console.log(arr.reduce((a, b) => a + b)) // 6
// The largest number in the array.
console.log(arr.reduce((a, b) => a > b ? a : b)) // 3

El reduce()método toma un segundo parámetro opcional, que es el valor inicial. Esto es útil cuando la matriz en la que llama reduce()puede tener cero o uno elementos. Por ejemplo, si quisiéramos crear una función sum()que tome una matriz como argumento y devuelva la suma de todos los elementos, podríamos escribirla así:

const sum = arr => arr.reduce((a, b) => a + b, 0)

console.log(sum([]))     // 0
console.log(sum([4]))    // 4
console.log(sum([2, 5])) // 7

Michał Perłakowski
fuente
Esta es mi respuesta favorita. También podría agregar un ejemplo para el bucle solo un dato anidado específico, comoObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
SilverSurfer el
25

A veces, puede ser conveniente acceder a un objeto anidado utilizando una cadena. El enfoque simple es el primer nivel, por ejemplo

var obj = { hello: "world" };
var key = "hello";
alert(obj[key]);//world

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

var obj = {
 arr: [
    { id: 1, name: "larry" },    
    { id: 2, name: "curly" },
    { id: 3, name: "moe" }
 ]
};

Y sabe que desea obtener el primer resultado de la matriz en el objeto, tal vez le gustaría usar

var moe = obj["arr[0].name"];

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.

function flatten(obj){
 var root = {};
 (function tree(obj, index){
   var suffix = toString.call(obj) == "[object Array]" ? "]" : "";
   for(var key in obj){
    if(!obj.hasOwnProperty(key))continue;
    root[index+key+suffix] = obj[key];
    if( toString.call(obj[key]) == "[object Array]" )tree(obj[key],index+key+suffix+"[");
    if( toString.call(obj[key]) == "[object Object]" )tree(obj[key],index+key+suffix+".");   
   }
 })(obj,"");
 return root;
}

Ahora, el objeto complejo puede ser aplanado

var obj = previous definition;
var flat = flatten(obj);
var moe = flat["arr[0].name"];//moe

Aquí se jsFiddle Demoestá utilizando uno de estos enfoques.

Travis J
fuente
¿Qué quieres usar en obj["arr[0].name"]lugar de obj.arr[0].name? Apenas necesita / desea lidiar con objetos aplanados, excepto para la serialización.
Bergi
@ Bergi: veo esta pregunta con frecuencia, y dado que se está utilizando canónicamente, publiqué una respuesta a esa versión. Si es evitable, es mucho más rápido usar obj.arr [0] .name, pero a veces la gente quiere pasar accesores de cadena y este es un ejemplo de ello.
Travis J
Urgh Aún así, apenas hay una razón para aplanar el objeto completo solo para usar una sola ruta de cadena, simplemente podría analizar eso y hacer una búsqueda dinámica.
Bergi
14

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.

const data = {
  code: 42,
  items: [{
    id: 1,
    name: 'foo'
  }, {
    id: 2,
    name: 'bar'
  }]
};

const {
  items: [, {
    name: secondName
  }]
} = data;

console.log(secondName);

El ejemplo anterior crea una variable llamada secondNamedesde la nameclave desde una matriz llamada items, 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.

Alex KeySmith
fuente
13

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:

data['items'][1]['name']

estos también funcionan

data.items[1].name
data['items'][1].name
data.items[1]['name']

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 forbucle, pero hay una manera muy simple de recorrer una ruta usando Array.reduce.

const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }
const path = [ 'items', '1', 'name']
let result = path.reduce((a,v) => a[v], data)

La ruta es una forma de decir: Primero tome el objeto con clave items, que resulta ser una matriz. Luego tome el 1elemento -st (0 arrays de índice). Por último, tome el objeto con clave nameen ese elemento de matriz, que resulta ser la cadena bar.

Si tiene un camino muy largo, incluso podría utilizarlo String.splitpara facilitar todo esto:

'items.1.name'.split('.').reduce((a,v) => a[v], data)

Esto es simplemente JavaScript, sin usar bibliotecas de terceros como jQuery o lodash.

Evgeny
fuente
13
var ourStorage = {


"desk":    {
    "drawer": "stapler"
  },
"cabinet": {
    "top drawer": { 
      "folder1": "a file",
      "folder2": "secrets"
    },
    "bottom drawer": "soda"
  }
};
ourStorage.cabinet["top drawer"].folder2; // Outputs -> "secrets"

o

//parent.subParent.subsubParent["almost there"]["final property"]

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í:

var ourPets = [
  {
    animalType: "cat",
    names: [
      "Meowzer",
      "Fluffy",
      "Kit-Cat"
    ]
  },
  {
    animalType: "dog",
    names: [
      "Spot",
      "Bowser",
      "Frankie"
    ]
  }
];
ourPets[0].names[1]; // Outputs "Fluffy"
ourPets[1].names[0]; // Outputs "Spot"

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

Johnny
fuente
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia. - De la opinión
Robert
1
Edité la publicación. Aunque la gente se apresuró a darle mala reputación. La próxima vez me abstendré de dar una respuesta.
Johnny
1
@Riddick no se abstenga, solo asegúrese de no publicar solo un enlace
reggaeguitar
12

Puedes usar la lodash _getfunción:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3
Sergey
fuente
9

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:

$..items[1].name

entonces:

var secondName = jsonPath.eval(data, "$..items[1].name");
Andrejs
fuente
1
Usar eval () no es una buena solución. En su lugar, se puede utilizar la función de primera clase.
pradeep gowda
8

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

const name = ((user || {}).personalInfo || {}).name;

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.

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'addresses', 0, 'city']);
// this will return the city from the first address item.

También hay un tipo excelente que maneja el tipo de biblioteca mínimo que hace todo esto por usted.

Dinesh Pandiyan
fuente
3
Esta pregunta es principalmente sobre las propiedades de acceso que existen. Ya hay una pregunta sobre a qué se refiere (y ya incluye la mayoría de sus soluciones): Acceda a los objetos anidados de Javascript de forma segura o Acceda a los objetos de JavaScript anidados con la clave de cadena . Pero de todos modos: "Desafortunadamente, no puedes acceder a matrices anidadas con este truco". Por qué no? Las matrices son objetos, por lo que debería funcionar igual de bien. ¿Puedes dar un ejemplo donde no lo hace?
Felix Kling
1
@FelixKling Cuando intentamos acceder a las matrices con el patrón Oliver Steele, no podremos crear una matriz 'n' de longitud sobre la marcha y acceder al enésimo índice sin obtener un error 'indefinido'. Ex. ((user || {}).address || new Array(3))[1].name
Dinesh Pandiyan
3
No estás aplicando tu patrón consistentemente. Por supuesto, ...[1].barse produciría un error si el elemento 1no existiera. Pero ese también es el caso de ....foo.barsi foono existiera. También debe "proteger" el acceso 1, 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.
Felix Kling
1
Esto es genial. No me pareció de esta manera. Gracias a @FelixKling, iré a actualizar las publicaciones del blog.
Dinesh Pandiyan
2
@DineshPandiyan deberías revelar que eres el autor de typy, acabo de llegar aquí después de leer la publicación de tu blog
reggaeguitar
8

Prefiero JQuery. Es más limpio y fácil de leer.

$.each($.parseJSON(data), function (key, value) {
  alert(value.<propertyname>);
});
Rudy Hinojosa
fuente
7

Acceder dinámicamente a objetos de múltiples niveles.

var obj = {
  name: "john doe",
  subobj: {
    subsubobj: {
      names: "I am sub sub obj"
    }
  }
};

var level = "subobj.subsubobj.names";
level = level.split(".");

var currentObjState = obj;

for (var i = 0; i < level.length; i++) {
  currentObjState = currentObjState[level[i]];
}

console.log(currentObjState);

Violín de trabajo: https://jsfiddle.net/andreitodorut/3mws3kjL/

Andrei Todorut
fuente
6

Si está buscando uno o más objetos que cumplan ciertos criterios, tiene algunas opciones con query-js

//will return all elements with an id larger than 1
data.items.where(function(e){return e.id > 1;});
//will return the first element with an id larger than 1
data.items.first(function(e){return e.id > 1;});
//will return the first element with an id larger than 1 
//or the second argument if non are found
data.items.first(function(e){return e.id > 1;},{id:-1,name:""});

También hay a singley a singleOrDefault, funcionan de manera similar firsty firstOrDefaultrespectivamente. 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

Runa FS
fuente
Me encantaría saber cómo se podría mejorar esto. ¿Quieres dejar un comentario?
Rune FS
6

La forma de subrayar js

Que es una biblioteca de JavaScript que proporciona un montón de functional programmingayudantes útiles sin extender ningún objeto incorporado.

Solución:

var data = {
  code: 42,
  items: [{
    id: 1,
    name: 'foo'
  }, {
    id: 2,
    name: 'bar'
  }]
};

var item = _.findWhere(data.items, {
  id: 2
});
if (!_.isUndefined(item)) {
  console.log('NAME =>', item.name);
}

//using find - 

var item = _.find(data.items, function(item) {
  return item.id === 2;
});

if (!_.isUndefined(item)) {
  console.log('NAME =>', item.name);
}
Mohan Dere
fuente
6

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

_.get(response, ['output', 'fund', 'data', '0', 'children', '0', 'group', 'myValue'], '')

igual que:

Opt 2

response.output.fund.data[0].children[0].group.myValue

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 regular filter(). 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.

Thiago C. S Ventura
fuente
5

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'.

var data = {
  code: 42,
  items: [{
    id: 1,
    name: 'aaa',
    items: [{
        id: 3,
        name: 'ccc'
      }, {
        id: 4,
        name: 'ddd'
      }]
    }, {
    id: 2,
    name: 'bbb',
    items: [{
        id: 5,
        name: 'eee'
      }, {
        id: 6,
        name: 'fff'
      }]
    }]
};

var jsonloop = new JSONLoop(data, 'id', 'items');

jsonloop.findNodeById(data, 5, function(err, node) {
  if (err) {
    document.write(err);
  } else {
    document.write(JSON.stringify(node, null, 2));
  }
});
<script src="https://rawgit.com/dabeng/JSON-Loop/master/JSONLoop.js"></script>

dabeng
fuente
¿Cómo accedo al objeto json anidado usando variables? datos = {a: {b: 'ss'}}; var key = ab data [key] no funciona
Pasupathi Rajamanickam
3

Puede usar la sintaxis jsonObject.keypara acceder al valor. Y si desea acceder a un valor desde una matriz, puede usar la sintaxis jsonObjectArray[index].key.

Aquí están los ejemplos de código para acceder a varios valores para darle la idea.

        var data = {
            code: 42,
            items: [{
                id: 1,
                name: 'foo'
            }, {
                id: 2,
                name: 'bar'
            }]
        };

        // if you want 'bar'
        console.log(data.items[1].name);

        // if you want array of item names
        console.log(data.items.map(x => x.name));

        // get the id of the item where name = 'bar'
        console.log(data.items.filter(x => (x.name == "bar") ? x.id : null)[0].id);

Rahul Vala
fuente
3

Enfoque dinámico

En la deep(data,key)función a continuación , puede usar una keycadena arbitraria , en su caso items[1].name(puede usar la notación de matriz [i]en cualquier nivel), si la clave no es válida, se devuelve indefinido.

Kamil Kiełczewski
fuente
2

Un enfoque pitónico, recursivo y funcional para desentrañar árboles JSON arbitrarios:

handlers = {
    list:  iterate,
    dict:  delve,
    str:   emit_li,
    float: emit_li,
}

def emit_li(stuff, strong=False):
    emission = '<li><strong>%s</strong></li>' if strong else '<li>%s</li>'
    print(emission % stuff)

def iterate(a_list):
    print('<ul>')
    map(unravel, a_list)
    print('</ul>')

def delve(a_dict):
    print('<ul>')
    for key, value in a_dict.items():
        emit_li(key, strong=True)
        unravel(value)
    print('</ul>')

def unravel(structure):
    h = handlers[type(structure)]
    return h(structure)

unravel(data)

donde los datos son una lista de python (analizada a partir de una cadena de texto JSON):

data = [
    {'data': {'customKey1': 'customValue1',
           'customKey2': {'customSubKey1': {'customSubSubKey1': 'keyvalue'}}},
  'geometry': {'location': {'lat': 37.3860517, 'lng': -122.0838511},
               'viewport': {'northeast': {'lat': 37.4508789,
                                          'lng': -122.0446721},
                            'southwest': {'lat': 37.3567599,
                                          'lng': -122.1178619}}},
  'name': 'Mountain View',
  'scope': 'GOOGLE',
  'types': ['locality', 'political']}
]
pX0r
fuente
66
Esta pregunta es sobre JavaScript, no sobre Python. No estoy seguro de si hay una pregunta equivalente para Python.
Felix Kling
2

La función grep de jQuery le permite filtrar a través de una matriz:

var data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

$.grep(data.items, function(item) {
    if (item.id === 2) {
        console.log(item.id); //console id of item
        console.log(item.name); //console name of item
        console.log(item); //console item object
        return item; //returns item object
    }

});
// Object {id: 2, name: "bar"}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


fuente
2
// const path = 'info.value[0].item'
// const obj = { info: { value: [ { item: 'it works!' } ], randominfo: 3 }  }
// getValue(path, obj)

export const getValue = ( path , obj) => {
  const newPath = path.replace(/\]/g, "")
  const arrayPath = newPath.split(/[\[\.]+/) || newPath;

  const final = arrayPath.reduce( (obj, k) => obj ?  obj[k] : obj, obj)
  return final;
}
Michael Dimmitt
fuente
-4

Mi stringdataproviene de un archivo PHP, pero aún así, indico aquí en var. Cuando tomo directamente mi json obj, no se mostrará nada, por eso pongo mi archivo json como

var obj=JSON.parse(stringdata); así que después de eso obtengo messageobj y muestro en el cuadro de alerta, obtengo datacuál es la matriz json y la almaceno en una variable, ArrObjluego leo el primer objeto de esa matriz con un valor clave como esteArrObj[0].id

     var stringdata={
        "success": true,
        "message": "working",
        "data": [{
                  "id": 1,
                  "name": "foo"
         }]
      };

                var obj=JSON.parse(stringdata);
                var key = "message";
                alert(obj[key]);
                var keyobj = "data";
                var ArrObj =obj[keyobj];

                alert(ArrObj[0].id);
Manthan Patel
fuente
2
El ejemplo es confuso porque stringjsonno es una cadena.
Felix Kling