Como acessar e processar objetos aninhados, matrizes ou JSON?

875

Eu tenho uma estrutura de dados aninhada contendo objetos e matrizes. Como posso extrair as informações, ou seja, acessar valores (ou chaves) específicos ou múltiplos?

Por exemplo:

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

Como acessar nameo segundo item items?

Felix Kling
fonte
22
@ Marcel: Ele deve ser lido como "Eu tenho uma estrutura de dados aninhada ou JSON, como posso acessar um valor específico?". Eu sei a diferença, mas muitas pessoas não sabem e podem estar procurando por "JSON" em vez de "objeto". Na verdade, muitas perguntas têm o formato "como posso acessar o X neste JSON". O único lugar em que menciono JSON na minha resposta é onde explico o que é. Se você tem uma sugestão de como comunicar isso de uma maneira melhor, sou todo ouvidos.
Felix Kling
possível duplicata de JSON achado em JavaScript
Travis J

Respostas:

1160

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 -> valueestrutura. 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 nameo 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 itemspropriedade é 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 namepropriedade. 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 undefinedvolta?

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.logou console.dirinspecione 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...incom 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 forloop:

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...inpara 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...ofnos 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);

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 toArraynovamente 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;
}



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, namee __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.

Felix Kling
fonte
3
Parte do que está sendo vinculado aqui está realmente perguntando como fazer isso no jQuery, o que para ser justo simplifica 1 ou 2 coisas aqui. Não tenho certeza se deve aumentar mais de um megapost ou responder separadamente - os princípios abordados aqui sobre o que é um objeto e o que é uma matriz são geralmente o que realmente está sendo solicitado ....
Chris Moschini
1
@ felix-kling Uma coisa ... com objetos aninhados, como let object = {a: 1, b: 2, c: { a: 3, b: 4 }};, isso retorna uma matriz contendo uma matriz para cada objeto aninhado, nesse caso, [ 1, 2, [ 3, 4 ] ]não seria melhor usar concat na chamada recursiva em vez de push? (exigindo resultado a ser mutável)
ElFitz
3
Esta é a resposta mais profunda que já vi no Stack Overflow - e respondeu à minha pergunta! Obrigado!
22618 William Hill Jones
essa página me fez aprender a diferença entre ARRAY e OBJ
4ni5 19/02
76

Você pode acessá-lo desta maneira

data.items[1].name

ou

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

Ambas as formas são iguais.

vitmalina
fonte
Sim, mas você não pode fazer dados ["itens"]. 1.name
neaumusic
5
Primeiro, é muito mais intuitivo, legível e mais curto;) Prefiro usar a sintaxe da propriedade bracket apenas quando o nome da propriedade é variável.
DanteTheSmith 02/10/19
35

Caso você esteja tentando acessar uma itemestrutura de exemplo usando idou name, sem saber sua posição na matriz, a maneira mais fácil de fazer isso seria usar a 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"}

Pela minha experiência, o uso de funções de ordem superior em vez de forou em for..inloop resulta em um código mais fácil de raciocinar e, portanto, mais sustentável.

Apenas meus 2 centavos.

princípio holográfico
fonte
29

Objetos e matrizes tem muitos métodos internos que podem ajudá-lo no processamento de dados.

Nota: em muitos exemplos, estou usando funções de seta . Eles são semelhantes às expressões de função , mas vinculam o thisvalor lexicamente.

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

Object.keys()retorna uma matriz de chaves do objeto, Object.values()retorna uma matriz de valores do objeto e Object.entries()retorna uma matriz de chaves do objeto e valores correspondentes em um 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() com um loop for-of e atribuição de desestruturação

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

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

É muito conveniente iterar o resultado Object.entries()com uma atribuição de loop for-of e desestruturação .

O loop for-of permite iterar elementos da matriz. A sintaxe é for (const element of array)(podemos substituir constpor varou let, mas é melhor usá-lo constse não pretendemos modificar element).

A atribuição de reestruturação permite extrair valores de uma matriz ou objeto e atribuí-los a variáveis. Nesse caso, const [key, value]significa que, em vez de atribuir a [key, value]matriz a element, atribuímos o primeiro elemento dessa matriz a keye o segundo elemento a value. É equivalente a isso:

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

Como você pode ver, a desestruturação torna isso muito mais simples.

Array.prototype.every() e Array.prototype.some()

O every()método retornará truese a função de retorno de chamada especificada retornar truepara todos os elementos da matriz. O some()método retornará truese a função de retorno de chamada especificada retornar truepara algum (pelo menos um) elemento.

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() e Array.prototype.filter()

Os find()métodos retornam o primeiro elemento que satisfaz a função de retorno de chamada fornecida. O filter()método retorna uma matriz de todos os elementos que satisfazem a função de retorno de chamada fornecida.

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()

O map()método retorna uma matriz com os resultados da chamada de uma função de retorno de chamada fornecida nos elementos da 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()

O reduce()método reduz uma matriz para um único valor chamando a função de retorno de chamada fornecida com dois 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

O reduce()método usa um segundo parâmetro opcional, que é o valor inicial. Isso é útil quando a matriz que você chama reduce()pode ter zero ou um elemento. Por exemplo, se quiséssemos criar uma função sum()que pega uma matriz como argumento e retorna a soma de todos os elementos, poderíamos escrevê-la assim:

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
fonte
Esta é a minha resposta favorita. Você também pode adicionar um exemplo para o laço apenas uma expecific aninhados dados, comoObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
silversurfer
25

Às vezes, acessar um objeto aninhado usando uma string pode ser desejável. A abordagem simples é o primeiro nível, por exemplo

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

Mas esse geralmente não é o caso do json complexo. À medida que o json se torna mais complexo, as abordagens para encontrar valores dentro do json também se tornam complexas. É melhor uma abordagem recursiva para navegar no json, e como essa recursão é alavancada dependerá do tipo de dados que está sendo pesquisado. Se houver instruções condicionais envolvidas, uma pesquisa json pode ser uma boa ferramenta para usar.

Se a propriedade que está sendo acessada já for conhecida, mas o caminho for complexo, por exemplo, neste objeto

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

E você sabe que deseja obter o primeiro resultado da matriz no objeto, talvez você queira usar

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

No entanto, isso causará uma exceção, pois não há propriedade do objeto com esse nome. A solução para poder usar isso seria achatar o aspecto de árvore do objeto. Isso pode ser feito recursivamente.

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;
}

Agora, o objeto complexo pode ser achatado

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

Aqui está uma jsFiddle Demodessas abordagens sendo usadas.

Travis J
fonte
WTH você gostaria de usar em obj["arr[0].name"]vez de obj.arr[0].name? Você quase não precisa / deseja lidar com objetos achatados, exceto a serialização.
Bergi 19/08/19
@ Bergi - Eu vejo essa pergunta normalmente, e como ela está sendo usada canonicamente, postei uma resposta para essa versão. Se for evitável, é muito mais rápido usar obj.arr [0] .name, mas às vezes as pessoas querem passar acessadores de string e este é um exemplo disso.
Travis J
Urgh. Ainda assim, dificilmente há um motivo para achatar o objeto completo apenas para usar um único caminho de string; você pode simplesmente analisar isso e fazer uma pesquisa dinâmica.
Bergi
14

Esta questão é bastante antiga, portanto, como uma atualização contemporânea. Com o início do ES2015, existem alternativas para obter os dados necessários. Agora existe um recurso chamado destruição de objeto para acessar objetos aninhados.

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

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

console.log(secondName);

O exemplo acima cria uma variável chamada a secondNamepartir da namechave a partir de uma matriz chamada items, solitária, ,diz pular o primeiro objeto na matriz.

Notavelmente, é provavelmente um exagero para este exemplo, pois o acesso simples à matriz é mais fácil de ler, mas é útil ao separar objetos em geral.

Essa é uma introdução muito breve ao seu caso de uso específico. A desestruturação pode ser uma sintaxe incomum para se acostumar primeiro. Eu recomendo a leitura da documentação de Destruição das Tarefas da Mozilla para saber mais.

Alex KeySmith
fonte
13

Para acessar um atributo aninhado, é necessário especificar seu nome e, em seguida, procurar no objeto.

Se você já conhece o caminho exato, pode codificá-lo em seu script da seguinte maneira:

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

estes também funcionam -

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

Quando você não sabe o nome exato antes, ou um usuário é quem fornece o nome para você. É necessário pesquisar dinamicamente na estrutura de dados. Alguns sugeriram aqui que a pesquisa pode ser feita usando um forloop, mas existe uma maneira muito simples de percorrer um caminho 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)

O caminho é uma maneira de dizer: Primeiro, pegue o objeto com a chave items, que por acaso é uma matriz. Em seguida, pegue o 1elemento -st (0 matrizes de índice). Por último, pegue o objeto com a chave namenesse elemento da matriz, que passa a ser a string bar.

Se você tem um caminho muito longo, pode até usar isso String.splitpara facilitar tudo isso -

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

Isso é apenas JavaScript, sem o uso de bibliotecas de terceiros, como jQuery ou lodash.

Evgeny
fonte
13
var ourStorage = {


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

ou

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

Basicamente, use um ponto entre cada descendente que se desdobra embaixo dele e quando você tiver nomes de objetos compostos por duas cadeias, use a notação ["obj Name"]. Caso contrário, apenas um ponto seria suficiente;

Fonte: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects

para adicionar a isso, acessar matrizes aninhadas aconteceria da seguinte maneira:

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"

Fonte: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-arrays/

Outro documento mais útil que descreve a situação acima: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation

Acesso à propriedade via caminhada de pontos: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation

Johnny
fonte
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Revisão
Robert
1
Eu editei a postagem. Embora as pessoas fossem rápidas em dar uma má reputação a isso. Da próxima vez, evitarei dar uma resposta.
Johnny
1
@Riddick não abster-se, apenas certifique-se que você não postar apenas um elo
reggaeguitar
12

Você poderia usar a lodash _getfunção:

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

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

Usar JSONPath seria uma das soluções mais flexíveis se você estiver disposto a incluir uma biblioteca: https://github.com/s3u/JSONPath (nó e navegador)

Para o seu caso de uso, o caminho json seria:

$..items[1].name

tão:

var secondName = jsonPath.eval(data, "$..items[1].name");
Andrejs
fonte
1
Usar eval () não é uma boa solução. Em vez disso, a função de primeira classe pode ser usada.
Pradeep gowda #
8

Caso alguém esteja visitando esta pergunta em 2017 ou mais tarde e procurando uma maneira fácil de lembrar , aqui está uma publicação elaborada no blog Acessando Objetos Aninhados em JavaScript sem ser enganada por

Não é possível ler a propriedade 'foo' de erro indefinido

1. Padrão de acesso a objetos aninhados de Oliver Steele

A maneira mais fácil e limpa de usar o padrão de acesso a objetos aninhados de Oliver Steele

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

Com essa notação, você nunca encontrará

Não é possível ler a propriedade 'nome' de indefinido .

Você basicamente verifica se o usuário existe; caso contrário, você cria um objeto vazio em tempo real. Dessa forma, a chave do próximo nível sempre será acessada a partir de um objeto existente ou vazio , mas nunca indefinido.

2. Acesse objetos aninhados usando a matriz Reduzir

Para poder acessar matrizes aninhadas, você pode escrever seu próprio utilitário de redução de matriz.

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.

Há também um excelente tipo de manipulação mínima de typy de biblioteca que faz tudo isso para você.

Dinesh Pandiyan
fonte
3
Esta pergunta é principalmente sobre propriedades de acesso existentes. Já existe uma pergunta sobre o que você está se referindo (e já inclui a maioria de suas soluções): Acesse objetos aninhados Javascript com segurança ou Acessando objetos JavaScript aninhados com chave de cadeia . Mas de qualquer maneira: "Infelizmente, você não pode acessar matrizes aninhadas com esse truque." Por que não? Matrizes são objetos, portanto deve funcionar da mesma forma. Você pode fornecer um exemplo onde isso não acontece?
Felix Kling
1
@FelixKling Quando tentamos acessar matrizes com o padrão Oliver Steele, não podemos criar uma matriz com comprimento 'n' em tempo real e acessar o enésimo índice sem obter o erro 'indefinido'. Ex. ((user || {}).address || new Array(3))[1].name
Dinesh Pandiyan
3
Você não está aplicando seu padrão de forma consistente. Obviamente ...[1].bar, resultaria em um erro se o elemento 1não existisse. Mas esse também é o caso, ....foo.barse foonão existisse. Você também precisa "proteger" o acesso 1, assim como "protege" qualquer outro acesso à propriedade. Uma matriz é apenas um objeto. Um "elemento de matriz" é apenas uma propriedade. Aplicado corretamente, seria (((user || {}).address || {})[1] || {}).name.
Felix Kling
1
Isso é ótimo. Não me ocorreu assim. Obrigado @FelixKling, vou atualizar as postagens do blog.
Dinesh Pandiyan
2
@DineshPandiyan, você deve divulgar que é o autor do typy, acabei de chegar aqui depois de ler sua postagem no blog
reggaeguitar
8

Eu prefiro o JQuery. É mais limpo e fácil de ler.

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

Acessando dinamicamente objetos de vários níveis.

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);

Violino de trabalho: https://jsfiddle.net/andreitodorut/3mws3kjL/

Andrei Todorut
fonte
6

Se você estiver procurando por um ou mais objetos que atendam a certos critérios, você tem algumas opções usando 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:""});

Há também um singlee um, singleOrDefaulteles funcionam muito bem firste firstOrDefaultrespectivamente. A única diferença é que eles jogarão se mais de uma partida for encontrada.

para obter mais explicações sobre query-js, você pode começar com este post

Rune FS
fonte
Gostaria muito de saber como isso poderia ser melhorado. Gostaria de deixar um comentário?
Rune FS
6

The Underscore js Way

Que é uma biblioteca JavaScript que fornece toda uma bagunça de functional programmingajudantes úteis sem estender nenhum objeto interno.

Solução:

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
fonte
6

Pergunta antiga, mas como ninguém mencionou lodash (apenas sublinhado).

Caso você já esteja usando o lodash em seu projeto, acho uma maneira elegante de fazer isso em um exemplo complexo:

Opt 1

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

igual a:

Opt 2

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

A diferença entre a primeira e a segunda opção é que, na Opção 1, se uma das propriedades estiver faltando (indefinida) no caminho, você não receberá um erro, ela retornará o terceiro parâmetro.

Para filtro de matriz lodash tem, _.find()mas eu prefiro usar o regular filter(). Mas ainda acho que o método acima _.get()é super útil ao trabalhar com dados realmente complexos. Enfrentei no passado APIs realmente complexas e foi útil!

Espero que possa ser útil para quem está procurando opções para manipular dados realmente complexos que o título implica.

Thiago C. S Ventura
fonte
5

Não acho que o questionador se refira apenas a um objeto aninhado de nível, por isso apresento a seguinte demonstração para demonstrar como acessar o nó do objeto json profundamente aninhado. Tudo bem, vamos encontrar o nó com o 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
fonte
Como acesso o objeto json aninhado usando variáveis. data = {a: {b: 'ss'}}; var key = dados ab [key] não funciona
Pasupathi Rajamanickam
3

Você pode usar a sintaxe jsonObject.keypara acessar o valor. E se você deseja acessar um valor de uma matriz, pode usar a sintaxe jsonObjectArray[index].key.

Aqui estão os exemplos de código para acessar vários valores e dar a você a ideia.

        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
fonte
3

Abordagem dinâmica

Na deep(data,key)função abaixo , você pode usar uma keyseqüência arbitrária - no seu caso items[1].name(você pode usar a notação de matriz [i]em qualquer nível) - se a chave for inválida, o valor indefinido será retornado.

Kamil Kiełczewski
fonte
2

Uma abordagem pitônica, recursiva e funcional para desvendar árvores JSON arbitrárias:

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)

em que data é uma lista python (analisada a partir de uma sequência 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
fonte
6
Esta pergunta é sobre JavaScript, não Python. Não tenho certeza se existe uma pergunta equivalente para o Python.
Felix Kling
2

A função grep do jQuery permite filtrar através de uma 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>


fonte
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
fonte
-4

Minha stringdataestá vindo do arquivo PHP, mas ainda assim, indico aqui em var. Quando eu levo meu json diretamente para objele, nada mostrará por isso que eu coloquei meu arquivo json como

var obj=JSON.parse(stringdata); Então, depois que eu recebo messageobj e mostro na caixa de alerta, recebo dataqual é o array json e armazeno em uma variável, ArrObjentão leio o primeiro objeto desse array com um valor-chave 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
fonte
2
O exemplo é confuso porque stringjsonnão é uma string.
Felix Kling