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 -> value
struttura. 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 name
al 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 items
accede 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 name
proprietà. 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 undefined
indietro?
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.log
o console.dir
e 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...in
with 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 for
ciclo:
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...in
per 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...of
ci 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);
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);
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 toArray
nuovamente 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;
}
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));
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.items
tratta 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
, name
e __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.