Come posso accedere ed elaborare oggetti nidificati, array o JSON?


875

Ho una struttura di dati nidificata contenente oggetti e matrici. Come posso estrarre le informazioni, ovvero accedere a valori specifici o multipli (o chiavi)?

Per esempio:

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

Come posso accedere nameal secondo elemento in items?


22
@Marcel: deve essere letto come "Ho una struttura dati nidificata o JSON, come posso accedere a un valore specifico?". Io so la differenza, ma molte persone non e non potrebbe essere alla ricerca per "JSON" piuttosto che "oggetto". Molte domande in realtà hanno la forma "come posso accedere a X in questo JSON". L'unico posto in cui cito JSON nella mia risposta è dove spiego di cosa si tratta. Se hai un suggerimento su come comunicarlo in modo migliore, sono tutto orecchi.
Felix Kling

possibile duplicato di JSON trovato in JavaScript
Travis J

Risposte:


1160

Preliminari

JavaScript ha un solo tipo di dati che può contenere più valori: Oggetto . Una matrice è una forma speciale di oggetto.

(Pianura) Gli oggetti hanno la forma

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

Le matrici hanno la forma

[value, value, ...]

Sia le matrici che gli oggetti espongono una key -> valuestruttura. Le chiavi in ​​un array devono essere numeriche, mentre qualsiasi stringa può essere utilizzata come chiave negli oggetti. Le coppie chiave-valore sono anche chiamate "proprietà" .

È possibile accedere alle proprietà utilizzando la notazione a punti

const value = obj.someProperty;

o notazione tra parentesi , se il nome della proprietà non sarebbe un nome identificatore JavaScript valido [spec] , o il nome è il valore di una variabile:

// 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];

Per tale motivo, è possibile accedere agli elementi dell'array solo usando la notazione parentesi:

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

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

Aspetta ... che mi dici di JSON?

JSON è una rappresentazione testuale di dati, proprio come XML, YAML, CSV e altri. Per lavorare con tali dati, deve prima essere convertito in tipi di dati JavaScript, ovvero array e oggetti (e come è stato appena spiegato come lavorare con quelli). Come analizzare JSON è spiegato nella domanda Parse JSON in JavaScript? .

Ulteriore materiale di lettura

Come accedere a matrici e oggetti è una conoscenza JavaScript fondamentale e pertanto è consigliabile leggere la Guida JavaScript MDN , in particolare le sezioni



Accesso alle strutture dati nidificate

Una struttura di dati nidificata è una matrice o un oggetto che fa riferimento ad altre matrici o oggetti, ovvero i suoi valori sono matrici o oggetti. È possibile accedere a tali strutture applicando consecutivamente la notazione punto o parentesi.

Ecco un esempio:

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

Supponiamo di voler accedere nameal secondo elemento.

Ecco come possiamo farlo passo dopo passo:

Come possiamo vedere dataè un oggetto, quindi possiamo accedere alle sue proprietà usando la notazione a punti. Si itemsaccede alla proprietà come segue:

data.items

Il valore è un array, per accedere al suo secondo elemento, dobbiamo usare la notazione parentesi:

data.items[1]

Questo valore è un oggetto e usiamo nuovamente la notazione punto per accedere alla nameproprietà. Quindi alla fine otteniamo:

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

In alternativa, avremmo potuto usare la notazione parentesi per una qualsiasi delle proprietà, specialmente se il nome contenesse caratteri che lo avrebbero reso non valido per l'utilizzo della notazione punto:

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

Sto provando ad accedere a una proprietà ma torno solo undefinedindietro?

La maggior parte delle volte, quando si ottiene undefined, l'oggetto / array semplicemente non ha una proprietà con quel nome.

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

Utilizzare console.logo console.dire ispezionare la struttura dell'oggetto / matrice. La proprietà a cui stai tentando di accedere potrebbe essere effettivamente definita su un oggetto / array nidificato.

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

Cosa succede se i nomi delle proprietà sono dinamici e non li conosco in anticipo?

Se i nomi delle proprietà sono sconosciuti o si desidera accedere a tutte le proprietà di un oggetto / elementi di un array, è possibile utilizzare il ciclo for...in [MDN] per gli oggetti e il ciclo for [MDN] per gli array per scorrere su tutte le proprietà / elementi.

Oggetti

Per iterare su tutte le proprietà di data, possiamo iterare sull'oggetto in questo modo:

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
}

A seconda della provenienza dell'oggetto (e di ciò che si desidera fare), potrebbe essere necessario verificare in ogni iterazione se la proprietà è realmente una proprietà dell'oggetto o se è una proprietà ereditata. Puoi farlo con Object#hasOwnProperty [MDN] .

In alternativa a for...inwith hasOwnProperty, puoi usare Object.keys [MDN] per ottenere una matrice di nomi di proprietà :

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

Array

Per scorrere su tutti gli elementi data.items dell'array , utilizziamo un forciclo:

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`.
}

Si potrebbe anche usare for...inper scorrere su array, ma ci sono ragioni per cui questo dovrebbe essere evitato: Perché 'for (var item in list)' con array considerati cattive pratiche in JavaScript? .

Con il crescente supporto browser di ECMAScript 5, il metodo array forEach [MDN] diventa anche un'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)
}); 

In ambienti che supportano ES2015 (ES6), è anche possibile utilizzare il ciclo [MDN] , che non funziona solo per gli array, ma per tutti gli iterabili :for...of

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

In ogni iterazione, for...ofci fornisce direttamente il prossimo elemento dell'iterabile, non esiste un "indice" per accedere o utilizzare.


Cosa succede se la "profondità" della struttura dei dati non mi è nota?

Oltre alle chiavi sconosciute, anche la "profondità" della struttura dei dati (ovvero il numero di oggetti nidificati) potrebbe essere sconosciuta. Come accedere alle proprietà profondamente nidificate di solito dipende dalla struttura esatta dei dati.

Ma se la struttura dei dati contiene schemi ripetitivi, ad esempio la rappresentazione di un albero binario, la soluzione in genere include l'accesso ricorsivo [Wikipedia] a ciascun livello della struttura dei dati.

Ecco un esempio per ottenere il primo nodo foglia di un albero 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);

Un modo più generico per accedere a una struttura di dati nidificati con chiavi sconosciute e profondità è testare il tipo di valore e agire di conseguenza.

Ecco un esempio che aggiunge tutti i valori primitivi all'interno di una struttura di dati nidificati in un array (supponendo che non contenga alcuna funzione). Se incontriamo un oggetto (o un array), semplicemente richiamiamo toArraynuovamente quel valore (chiamata ricorsiva).

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



Helpers

Poiché la struttura di un oggetto complesso o di una matrice non è necessariamente ovvia, possiamo esaminare il valore in ogni fase per decidere come spostarci ulteriormente. console.log [MDN] e console.dir [MDN] ci aiutano a farlo. Ad esempio (output della console di Chrome):

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

Qui vediamo che si data.itemstratta di una matrice con due elementi che sono entrambi oggetti. Nella console di Chrome gli oggetti possono anche essere espansi e controllati immediatamente.

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

Questo ci dice che data.items[1]è un oggetto e dopo averlo espanso vediamo che ha tre proprietà id, namee __proto__. Quest'ultima è una proprietà interna utilizzata per la catena prototipo dell'oggetto. Tuttavia, la catena di prototipi e l'ereditarietà non rientrano in questa risposta.


3
Parte di ciò che viene collegato qui sta davvero chiedendo come farlo in jQuery, che per essere onesti semplifica 1 o 2 cose qui. Non sono sicuro se rendere questo più di un megapost o rispondere a quelli separatamente - le basi coperte qui su ciò che è un oggetto che è un array sono di solito ciò che viene realmente chiesto ....
Chris Moschini,

1
@ felix-kling Una cosa ... con oggetti nidificati, come ad esempio let object = {a: 1, b: 2, c: { a: 3, b: 4 }};, questo restituisce una matrice contenente una matrice per ogni oggetto nidificato, in questo caso [ 1, 2, [ 3, 4 ] ]non sarebbe meglio usare concat nella chiamata ricorsiva anziché push? (richiedendo che il risultato sia mutabile)
ElFitz,

3
Questa è la risposta più approfondita che abbia mai visto su Stack Overflow e ha risposto alla mia domanda! Grazie!
William Jones,

questa pagina mi ha fatto imparare la differenza tra ARRAY e OBJ
4ni5

76

Puoi accedervi in ​​questo modo

data.items[1].name

o

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

Entrambe le vie sono uguali.


Sì, ma non puoi fare data ["items"]. 1.name
neaumusic

5
Il primo è molto più intuitivo, leggibile e più breve;) Preferisco usare la sintassi della proprietà parentesi solo quando il nome della proprietà è variabile.
DanteTheSmith,

35

Nel caso in cui si stia tentando di accedere a una itemstruttura di esempio da ido name, senza conoscerne la posizione nell'array, il modo più semplice per farlo sarebbe utilizzare la libreria 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"}

In base alla mia esperienza, l'utilizzo di funzioni di ordine superiore anziché foro for..inloop produce codice di cui è più facile ragionare e quindi più gestibile.

Solo i miei 2 centesimi.


29

Gli oggetti e le matrici hanno molti metodi integrati che possono aiutarti nell'elaborazione dei dati.

Nota: in molti degli esempi sto usando le funzioni freccia . Sono simili alle espressioni di funzione , ma legano il thisvalore in modo lessicale.

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

Object.keys()restituisce una matrice di chiavi dell'oggetto, Object.values()restituisce una matrice di valori dell'oggetto e Object.entries()restituisce una matrice di chiavi dell'oggetto e valori corrispondenti in 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 un ciclo for-of e un incarico distruttivo

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

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

È molto conveniente ripetere il risultato Object.entries()con un ciclo for-of e un incarico destrutturante .

Il ciclo For-of consente di iterare gli elementi dell'array. La sintassi è for (const element of array)(possiamo sostituire constcon varo let, ma è meglio usarla constse non intendiamo modificare element).

L'assegnazione di destrutturazione consente di estrarre valori da una matrice o un oggetto e assegnarli a variabili. In questo caso const [key, value]significa che invece di assegnare l' [key, value]array a element, assegniamo il primo elemento di quell'array keye il secondo elemento a value. È equivalente a questo:

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

Come puoi vedere, la destrutturazione rende tutto molto più semplice.

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

Il every()metodo restituisce truese la funzione di callback specificata ritorna trueper ogni elemento dell'array. Il some()metodo restituisce truese la funzione di richiamata specificata ritorna trueper alcuni (almeno un) 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()

Il find()metodo restituisce il primo elemento che soddisfa la funzione di richiamata fornita. Il filter()metodo restituisce una matrice di tutti gli elementi che soddisfa la funzione di richiamata fornita.

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

Il map()metodo restituisce un array con i risultati della chiamata di una funzione di callback fornita sugli elementi dell'array.

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

Il reduce()metodo riduce un array a un singolo valore chiamando la funzione di richiamata fornita con due elementi.

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

Il reduce()metodo accetta un secondo parametro facoltativo, che è il valore iniziale. Ciò è utile quando l'array su cui si chiama reduce()può avere zero o uno elementi. Ad esempio, se volessimo creare una funzione sum()che accetta una matrice come argomento e restituisce la somma di tutti gli elementi, potremmo scriverla in questo modo:

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


Questa è la mia risposta preferita Potresti anche aggiungere un esempio per il loop solo dei dati annidati espliciti, comeObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
SilverSurfer,

25

A volte, è consigliabile accedere a un oggetto nidificato utilizzando una stringa. L'approccio semplice è il primo livello, ad esempio

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

Ma questo non è spesso il caso di json complessi. Man mano che json diventa più complesso, anche gli approcci per trovare valori all'interno di json diventano complessi. È preferibile un approccio ricorsivo per la navigazione nel json e il modo in cui viene sfruttata la ricorsione dipenderà dal tipo di dati ricercati. Se sono presenti dichiarazioni condizionali, una ricerca JSON può essere un buon strumento da utilizzare.

Se la proprietà a cui si accede è già nota, ma il percorso è complesso, ad esempio in questo oggetto

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

E sai che vuoi ottenere il primo risultato dell'array nell'oggetto, forse vorresti usarlo

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

Tuttavia, ciò provocherà un'eccezione in quanto non esiste alcuna proprietà dell'oggetto con quel nome. La soluzione per poterlo utilizzare sarebbe appiattire l'aspetto dell'albero dell'oggetto. Questo può essere fatto in modo ricorsivo.

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

Ora, l'oggetto complesso può essere appiattito

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

Ecco uno jsFiddle Demodi questo approccio in uso.


Cosa vorresti usare al obj["arr[0].name"]posto di obj.arr[0].name? Difficilmente hai bisogno / vuoi occuparti di oggetti appiattiti ad eccezione della serializzazione.
Bergi,

@Bergi - Vedo questa domanda comunemente, e poiché questa viene utilizzata canonicamente, ho pubblicato una risposta a quella versione. Se è evitabile, è molto più veloce usare obj.arr [0] .name, ma a volte le persone vogliono passare gli accessi alle stringhe e questo è un esempio di ciò.
Travis J,

Urgh. Tuttavia, non c'è quasi motivo di appiattire l'intero oggetto solo per utilizzare un singolo percorso di stringa, è possibile semplicemente analizzarlo e fare una ricerca dinamica.
Bergi,

14

Questa domanda è piuttosto vecchia, quindi come un aggiornamento contemporaneo. Con l'inizio di ES2015 ci sono alternative per ottenere una sospensione dei dati richiesti. Ora esiste una funzione chiamata object destructuring per l'accesso agli oggetti nidificati.

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

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

console.log(secondName);

L'esempio sopra crea una variabile chiamata secondNamedalla namechiave da un array chiamato items, il solitario ,dice salta il primo oggetto dell'array.

In particolare, è probabilmente eccessivo per questo esempio, poiché un semplice accesso all'array è più facile da leggere, ma è utile quando si suddividono gli oggetti in generale.

Questa è un'introduzione molto breve al tuo caso d'uso specifico, la destrutturazione può essere una sintassi insolita a cui abituarsi all'inizio. Consiglio di leggere la documentazione di Mozilla Destructuring Assignment per saperne di più.


13

Per accedere a un attributo nidificato, è necessario specificarne il nome e quindi cercare attraverso l'oggetto.

Se conosci già il percorso esatto, puoi codificarlo nel tuo script in questo modo:

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

anche questi funzionano -

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

Quando non conosci il nome esatto prima o un utente è colui che ti fornisce il nome. Quindi è richiesta la ricerca dinamica attraverso la struttura dei dati. Alcuni hanno suggerito qui che la ricerca può essere eseguita utilizzando un forciclo, ma esiste un modo molto semplice per attraversare un percorso utilizzando 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)

Il percorso è un modo per dire: prima prendi l'oggetto con la chiave items, che sembra essere un array. Quindi prendere l' 1elemento -st (0 array di indici). Per ultimo prendi l'oggetto con la chiave namein quell'elemento array, che sembra essere la stringa bar.

Se hai un percorso molto lungo, potresti persino utilizzare String.splitper rendere tutto più semplice -

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

Questo è semplicemente JavaScript, senza utilizzare librerie di terze parti come jQuery o lodash.


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"]

Fondamentalmente, usa un punto tra ogni discendente che si svolge sotto di esso e quando hai nomi di oggetti composti da due stringhe, devi usare la notazione ["obj Name"]. Altrimenti, sarebbe sufficiente solo un punto;

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

per aggiungere a ciò, l'accesso alle matrici nidificate avverrebbe in questo modo:

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/

Un altro documento più utile che descrive la situazione sopra: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation

Accesso alla proprietà tramite dot walking: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
Robert,

1
Ho modificato il post. Anche se le persone sono state veloci a dargli una cattiva reputazione. La prossima volta mi asterrò dal dare una risposta.
Johnny,

1
@Riddick non astenersi, assicurati solo di non pubblicare solo un link
reggaeguitar

12

È possibile utilizzare la lodash _getfunzione:

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

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

9

L'uso di JSONPath sarebbe una delle soluzioni più flessibili se si desidera includere una libreria: https://github.com/s3u/JSONPath (nodo e browser)

Per il tuo caso d'uso il percorso json sarebbe:

$..items[1].name

così:

var secondName = jsonPath.eval(data, "$..items[1].name");

1
L'uso di eval () non è una buona soluzione. Invece è possibile utilizzare la funzione di prima classe.
pradeep gowda,

8

Per ogni evenienza, chiunque visiti questa domanda nel 2017 o successivamente e cerchi un modo facile da ricordare , ecco un elaborato post sul blog su Accesso agli oggetti nidificati in JavaScript senza essere confuso da

Impossibile leggere la proprietà "pippo" dell'errore non definito

1. Modello di accesso agli oggetti nidificati di Oliver Steele

Il modo più semplice e pulito è utilizzare il modello di accesso agli oggetti nidificati di Oliver Steele

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

Con questa notazione, non ti imbatterai mai

Impossibile leggere la proprietà 'nome' di undefined .

Fondamentalmente si controlla se l'utente esiste, in caso contrario, si crea un oggetto vuoto al volo. In questo modo, si accederà sempre alla chiave di livello successivo da un oggetto esistente o da un oggetto vuoto , ma mai da non definito.

2. Accedere agli oggetti nidificati utilizzando Riduci array

Per poter accedere ad array nidificati, è possibile scrivere il proprio array ridurre util.

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.

Esiste anche un tipo eccellente che gestisce un tipo di libreria minimale che fa tutto questo per te.


3
Questa domanda riguarda principalmente le proprietà di accesso esistenti. C'è già una domanda su ciò a cui ti riferisci (e già includendo la maggior parte delle tue soluzioni): accedi agli oggetti nidificati Javascript in modo sicuro o accedi agli oggetti JavaScript nidificati con la chiave stringa . Ma comunque: "Sfortunatamente, non puoi accedere ad array nidificati con questo trucco." Perchè no? Le matrici sono oggetti, quindi dovrebbe funzionare altrettanto bene. Potete fornire un esempio in cui non lo fa?
Felix Kling

1
@FelixKling Quando proviamo ad accedere ad array con pattern Oliver Steele, non saremo in grado di creare al volo l'array su 'n' length e accedere all'n-index senza ottenere errori 'indefiniti'. Ex. ((user || {}).address || new Array(3))[1].name
Dinesh Pandiyan,

3
Non stai applicando il tuo modello in modo coerente. Naturalmente ...[1].barsi tradurrebbe in un errore se l'elemento 1non esistesse. Ma è anche il caso ....foo.barse foonon esistesse. Devi anche "proteggere" l'accesso 1, proprio come "custodisci" qualsiasi altro accesso alla proprietà. Un array è solo un oggetto. Un "elemento array" è solo una proprietà. Sarebbe applicato correttamente (((user || {}).address || {})[1] || {}).name.
Felix Kling,

1
Questo è fantastico Non mi ha colpito in questo modo. Grazie a @FelixKling, andrò ad aggiornare i post del blog.
Dinesh Pandiyan,

2
@DineshPandiyan dovresti rivelare che sei l'autore di typy, sono appena arrivato qui dopo aver letto il tuo post sul blog
reggaeguitar,

8

Preferisco JQuery. È più pulito e facile da leggere.

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

7

Accesso dinamico a più livelli di oggetti.

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

Fiddle di lavoro: https://jsfiddle.net/andreitodorut/3mws3kjL/


6

Se stai cercando uno o più oggetti che soddisfano determinati criteri, hai alcune opzioni 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:""});

C'è anche un singlee un singleOrDefaultfunzionano molto come firste firstOrDefaultrispettivamente. L'unica differenza è che lanceranno se viene trovata più di una partita.

per ulteriori spiegazioni su query-js puoi iniziare con questo post


Mi piacerebbe sapere come questo potrebbe essere migliorato. Vuoi lasciare un commento?
Rune FS

6

The Underscore js Way

Che è una libreria JavaScript che fornisce tutta una serie di utili functional programminghelper senza estendere oggetti incorporati.

Soluzione:

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

6

Vecchia domanda ma come nessuno ha menzionato lodash (solo un trattino basso).

Nel caso in cui tu stia già utilizzando lodash nel tuo progetto, penso che un modo elegante per farlo in un esempio complesso:

Opt 1

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

uguale a:

Opt 2

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

La differenza tra la prima e la seconda opzione è che nell'Opzione 1 se manca una delle proprietà (non definita) nel percorso in cui non si ottiene un errore, viene restituito il terzo parametro.

Per array filtro lodash ha, _.find()ma preferirei usare il normale filter(). Ma penso ancora che il metodo sopra descritto _.get()sia molto utile quando si lavora con dati davvero complessi. In passato ho affrontato API davvero complesse ed è stato utile!

Spero possa essere utile per chi è alla ricerca di opzioni per manipolare dati davvero complessi che il titolo implica.


5

Non penso che l'interrogatore riguardi solo un oggetto nidificato di livello, quindi presento la seguente demo per dimostrare come accedere al nodo dell'oggetto json profondamente annidato. Va bene, troviamo il 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>


Come posso accedere all'oggetto json nidificato usando le variabili. data = {a: {b: 'ss'}}; var key = ab data [key] non funziona
Pasupathi Rajamanickam

3

È possibile utilizzare la sintassi jsonObject.keyper accedere al valore. E se si desidera accedere a un valore da un array, è possibile utilizzare la sintassi jsonObjectArray[index].key.

Ecco gli esempi di codice per accedere a vari valori per darti l'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);


3

Approccio dinamico

Nella deep(data,key)funzione di seguito , puoi usare una keystringa arbitraria - nel tuo caso items[1].name(puoi usare la notazione di array [i]a qualsiasi livello) - se la chiave non è valida, allora undefined è return.


2

Un approccio pitonico, ricorsivo e funzionale per svelare alberi JSON arbitrari:

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)

dove i dati sono un elenco Python (analizzato da una stringa di testo 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']}
]

6
Questa domanda riguarda JavaScript, non Python. Non sono sicuro se esiste una domanda equivalente per Python.
Felix Kling,

2

La funzione grep di jQuery ti consente di filtrare attraverso un array:

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>


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


-4

Il mio stringdataproviene dal file PHP ma comunque, indico qui in var. Quando prendo direttamente il mio JSON in objesso non mostrerà nulla, ecco perché ho messo il mio file JSON come

var obj=JSON.parse(stringdata); quindi dopo ottengo messageobj e visualizzo nella casella di avviso quindi ottengo dataquale è l'array json e memorizzo in una variabile ArrObjquindi leggo il primo oggetto di quell'array con un valore chiave come questoArrObj[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);

2
L'esempio è confuso perché stringjsonnon è una stringa.
Felix Kling

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.