Preliminares
JavaScript possui apenas um tipo de dados que pode conter vários valores: Objeto . Uma matriz é uma forma especial de objeto.
(Simples) Os objetos têm a forma
{key: value, key: value, ...}
Matrizes têm o formato
[value, value, ...]
Matrizes e objetos expõem uma key -> value
estrutura. As chaves em uma matriz devem ser numéricas, enquanto qualquer sequência pode ser usada como chave nos objetos. Os pares de valores-chave também são chamados de "propriedades" .
As propriedades podem ser acessadas usando a notação de ponto
const value = obj.someProperty;
ou notação de colchete , se o nome da propriedade não for um nome de identificador JavaScript válido [spec] ou se o nome for o valor de uma variável:
// 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 esse motivo, os elementos da matriz só podem ser acessados usando a notação de colchete:
const value = arr[5]; // arr.5 would be a syntax error
// property name / index as variable
const x = 5;
const value = arr[x];
Espere ... e o JSON?
JSON é uma representação textual de dados, assim como XML, YAML, CSV e outros. Para trabalhar com esses dados, primeiro é necessário convertê-los em tipos de dados JavaScript, como arrays e objetos (e como trabalhar com eles foi explicado). Como analisar JSON é explicado na pergunta Analisar JSON em JavaScript? .
Material de leitura adicional
Como acessar matrizes e objetos é um conhecimento fundamental do JavaScript e, portanto, é aconselhável ler o MDN JavaScript Guide , especialmente as seções
Acessando estruturas de dados aninhadas
Uma estrutura de dados aninhada é uma matriz ou objeto que se refere a outras matrizes ou objetos, ou seja, seus valores são matrizes ou objetos. Essas estruturas podem ser acessadas aplicando consecutivamente a notação de ponto ou colchete.
Aqui está um exemplo:
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
Vamos supor que queremos acessar name
o segundo item.
Aqui está como podemos fazer isso passo a passo:
Como podemos ver data
é um objeto, portanto, podemos acessar suas propriedades usando a notação de pontos. A items
propriedade é acessada da seguinte forma:
data.items
O valor é uma matriz, para acessar seu segundo elemento, precisamos usar a notação entre colchetes:
data.items[1]
Este valor é um objeto e usamos a notação de ponto novamente para acessar a name
propriedade. Por fim, obtemos:
const item_name = data.items[1].name;
Como alternativa, poderíamos ter usado a notação de colchete para qualquer uma das propriedades, especialmente se o nome contivesse caracteres que o tornariam inválido para o uso da notação de ponto:
const item_name = data['items'][1]['name'];
Estou tentando acessar uma propriedade, mas recebo apenas de undefined
volta?
Na maioria das vezes, quando você está recebendo undefined
, o objeto / matriz simplesmente não possui uma propriedade com esse nome.
const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined
Use console.log
ou console.dir
inspecione a estrutura do objeto / matriz. A propriedade que você está tentando acessar pode estar realmente definida em um objeto / matriz aninhada.
console.log(foo.bar.baz); // 42
E se os nomes das propriedades forem dinâmicos e eu não os conhecermos de antemão?
Se os nomes das propriedades são desconhecidos ou queremos acessar todas as propriedades de um objeto / elementos de uma matriz, podemos usar o loop for...in
[MDN] para objetos e o loop for
[MDN] para matrizes para iterar todas as propriedades / elementos.
Objetos
Para iterar sobre todas as propriedades de data
, podemos iterar sobre o objeto da seguinte maneira:
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
}
Dependendo da origem do objeto (e o que você deseja fazer), talvez seja necessário testar em cada iteração se a propriedade é realmente uma propriedade do objeto ou se é uma propriedade herdada. Você pode fazer isso com Object#hasOwnProperty
[MDN] .
Como alternativa para for...in
com hasOwnProperty
, você pode usar Object.keys
[MDN] para obter uma variedade de nomes de propriedades :
Object.keys(data).forEach(function(prop) {
// `prop` is the property name
// `data[prop]` is the property value
});
Matrizes
Para iterar sobre todos os elementos da data.items
matriz , usamos um for
loop:
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`.
}
Também se poderia usar for...in
para iterar sobre matrizes, mas há razões para que isso deva ser evitado: Por que 'for (var item da lista)' com matrizes consideradas más práticas em JavaScript? .
Com o crescente suporte do ECMAScript 5 ao navegador, o método array forEach
[MDN] também se torna uma alternativa interessante:
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)
});
Em ambientes que suportam o ES2015 (ES6), você também pode usar o loop [MDN] , que não só funciona para matrizes, mas também para qualquer iterável :for...of
for (const item of data.items) {
// `item` is the array element, **not** the index
}
Em cada iteração, for...of
nos fornece diretamente o próximo elemento do iterável, não há "índice" para acessar ou usar.
E se a "profundidade" da estrutura de dados for desconhecida para mim?
Além das chaves desconhecidas, a "profundidade" da estrutura de dados (ou seja, quantos objetos aninhados) ela possui, também pode ser desconhecida. Como acessar propriedades profundamente aninhadas geralmente depende da estrutura de dados exata.
Mas se a estrutura de dados contiver padrões repetidos, por exemplo, a representação de uma árvore binária, a solução normalmente inclui o acesso recursivo [Wikipedia] a cada nível da estrutura de dados.
Aqui está um exemplo para obter o primeiro nó da folha de uma árvore binária:
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);
const root = {
leftChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 42
},
rightChild: {
leftChild: null,
rightChild: null,
data: 5
}
},
rightChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 6
},
rightChild: {
leftChild: null,
rightChild: null,
data: 7
}
}
};
function getLeaf(node) {
if (node.leftChild) {
return getLeaf(node.leftChild);
} else if (node.rightChild) {
return getLeaf(node.rightChild);
} else { // node must be a leaf node
return node;
}
}
console.log(getLeaf(root).data);
Uma maneira mais genérica de acessar uma estrutura de dados aninhada com chaves e profundidade desconhecidas é testar o tipo do valor e agir de acordo.
Aqui está um exemplo que adiciona todos os valores primitivos dentro de uma estrutura de dados aninhados a uma matriz (supondo que ela não contenha nenhuma função). Se encontrarmos um objeto (ou matriz), simplesmente chamaremos toArray
novamente esse valor (chamada 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;
}
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value));
} else {
result.push(value);
}
}
return result;
}
console.log(toArray(data));
Ajudantes
Como a estrutura de um objeto ou matriz complexo não é necessariamente óbvia, podemos inspecionar o valor em cada etapa para decidir como avançar. console.log
[MDN] e console.dir
[MDN] nos ajudam a fazer isso. Por exemplo (saída do console do Chrome):
> console.log(data.items)
[ Object, Object ]
Aqui vemos que essa data.items
é uma matriz com dois elementos que são ambos objetos. No console do Chrome, os objetos podem ser expandidos e inspecionados imediatamente.
> console.log(data.items[1])
Object
id: 2
name: "bar"
__proto__: Object
Isso nos diz que data.items[1]
é um objeto e, após expandi-lo, vemos que ele possui três propriedades id
, name
e __proto__
. Esta última é uma propriedade interna usada para a cadeia de protótipos do objeto. A cadeia de protótipo e a herança estão fora do escopo desta resposta.