1034 votes

Comment puis-je accéder et traiter des objets imbriqués, des tableaux ou du JSON ?

J'ai une structure de données imbriquée contenant des objets et des tableaux. Comment puis-je extraire les informations, c'est-à-dire accéder à une ou plusieurs valeurs (ou clés) spécifiques ?

Par exemple :

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

Comment pourrais-je accéder au name du deuxième élément dans items ?

28 votes

@Marcel : Il faut le lire comme suit : "J'ai une structure de données imbriquées ou JSON, comment puis-je accéder à une valeur spécifique ?". I connaissent la différence, mais beaucoup de gens ne la connaissent pas et peuvent chercher "JSON" plutôt que "objet". De nombreuses questions sont en fait du type "comment puis-je accéder à X dans ce JSON". Le seul endroit où je mentionne JSON dans ma réponse est lorsque j'explique ce que c'est. Si vous avez une suggestion pour mieux communiquer, je suis tout ouïe.

0 votes

Duplicata possible de Recherche JSON en JavaScript

0 votes

Cette réponse m'a aidé à résoudre le problème de l'accès aux objets imbriqués de façon très agréable et claire : stackoverflow.com/questions/6491463/ Permet d'écrire par exemple : someObject.access("firstPart[2].someOtherPart.myId")

1365voto

Felix Kling Points 247451

Préliminaires

JavaScript ne possède qu'un seul type de données qui peut contenir plusieurs valeurs : Objet . Un site Array est une forme particulière d'objet.

(Plaine) Les objets ont la forme

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

Les tableaux ont la forme

[value, value, ...]

Les tableaux et les objets présentent tous deux une fonction key -> value structure. Les clés d'un tableau doivent être numériques, tandis que toute chaîne de caractères peut être utilisée comme clé dans les objets. Les paires clé-valeur sont également appelées "propriétés" .

On peut accéder aux propriétés soit en utilisant notation par points

const value = obj.someProperty;

ou notation entre crochets si le nom de la propriété n'est pas un nom JavaScript valide. nom de l'identifiant [spec] ou le nom est la valeur d'une 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];

C'est pourquoi on ne peut accéder aux éléments d'un tableau qu'en utilisant la notation entre parenthèses :

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

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

Attendez... et JSON ?

JSON est une représentation textuelle des données, tout comme XML, YAML, CSV et autres. Pour travailler avec de telles données, il faut d'abord les convertir en types de données JavaScript, c'est-à-dire en tableaux et en objets (la façon de travailler avec ces derniers vient d'être expliquée). La façon d'analyser JSON est expliquée dans la question suivante Analyser JSON en JavaScript ? .

Matériel de lecture supplémentaire

La manière d'accéder aux tableaux et aux objets est une connaissance fondamentale de JavaScript et il est donc conseillé de lire le manuel Guide MDN du JavaScript en particulier les sections



Accès aux structures de données imbriquées

Une structure de données imbriquée est un tableau ou un objet qui fait référence à d'autres tableaux ou objets, c'est-à-dire que ses valeurs sont des tableaux ou des objets. On peut accéder à de telles structures en appliquant consécutivement la notation par points ou par crochets.

Voici un exemple :

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

Supposons que nous voulions accéder au name du deuxième élément.

Voici comment procéder, étape par étape :

Comme on peut le voir data est un objet, nous pouvons donc accéder à ses propriétés en utilisant la notation point. Le site items est accessible comme suit :

data.items

La valeur est un tableau, pour accéder à son deuxième élément, nous devons utiliser la notation entre parenthèses :

data.items[1]

Cette valeur est un objet et nous utilisons à nouveau la notation point pour accéder à l'objet name propriété. Donc on finit par obtenir :

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

Nous aurions également pu utiliser la notation par points pour toutes les propriétés, surtout si le nom contenait des caractères qui l'auraient rendu invalide pour l'utilisation de la notation par points :

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

J'essaie d'accéder à une propriété mais je n'obtiens que undefined de retour ?

La plupart du temps, lorsque vous obtenez undefined l'objet/le tableau n'a tout simplement pas de propriété portant ce nom.

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

Utilisez console.log ou console.dir et inspecter la structure de l'objet / du tableau. La propriété à laquelle vous essayez d'accéder peut être en fait définie sur un objet / tableau imbriqué.

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

Que faire si les noms des propriétés sont dynamiques et que je ne les connais pas à l'avance ?

Si les noms des propriétés sont inconnus ou si nous voulons accéder à toutes les propriétés d'un objet / éléments d'un tableau, nous pouvons utiliser la fonction for...in [MDN] pour les objets et la for [MDN] boucle pour les tableaux afin d'itérer sur toutes les propriétés / éléments.

Objets

Pour itérer sur toutes les propriétés de data nous pouvons itérer sur les objet comme ça :

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
}

En fonction de l'origine de l'objet (et de ce que vous voulez faire), il se peut que vous deviez tester à chaque itération si la propriété est réellement une propriété de l'objet, ou si c'est une propriété héritée. Vous pouvez le faire avec Object#hasOwnProperty [MDN] .

Comme alternative à for...in avec hasOwnProperty vous pouvez utiliser Object.keys [MDN] pour obtenir un tableau de noms de propriétés :

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

Tableaux

Pour itérer sur tous les éléments de la data.items tableau nous utilisons un for boucle :

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

On pourrait également utiliser for...in pour itérer sur des tableaux, mais il y a des raisons pour lesquelles cela devrait être évité : Pourquoi la méthode "for(var item in list)" avec des tableaux est-elle considérée comme une mauvaise pratique en JavaScript ? .

Avec la prise en charge croissante de l'ECMAScript 5 par les navigateurs, la méthode de tableau forEach [MDN] devient également une alternative intéressante :

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

Dans les environnements prenant en charge ES2015 (ES6), vous pouvez également utiliser l'option for...of [MDN] qui fonctionne non seulement pour les tableaux, mais aussi pour n'importe quelle itérable :

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

Dans chaque itération, for...of nous donne directement l'élément suivant de l'itérable, il n'y a pas d'"index" à accéder ou à utiliser.


Et si je ne connais pas la "profondeur" de la structure de données ?

Outre les clés inconnues, la "profondeur" de la structure de données (c'est-à-dire le nombre d'objets imbriqués) peut également être inconnue. La façon d'accéder aux propriétés profondément imbriquées dépend généralement de la structure de données exacte.

Mais si la structure de données contient des motifs répétitifs, par exemple la représentation d'un arbre binaire, la solution consiste généralement à récursivement [Wikipedia] accéder à chaque niveau de la structure de données.

Voici un exemple pour obtenir le premier nœud de feuille d'un arbre binaire :

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

Une manière plus générique d'accéder à une structure de données imbriquée dont les clés et la profondeur sont inconnues consiste à tester le type de la valeur et à agir en conséquence.

Voici un exemple qui ajoute toutes les valeurs primitives d'une structure de données imbriquée dans un tableau (en supposant qu'elle ne contient pas de fonctions). Si nous rencontrons un objet (ou un tableau), nous appelons simplement toArray à nouveau sur cette valeur (appel récursif).

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


Aides

Comme la structure d'un objet ou d'un tableau complexe n'est pas nécessairement évidente, nous pouvons inspecter la valeur à chaque étape pour décider comment aller plus loin. console.log [MDN] et console.dir [MDN] aidez-nous à le faire. Par exemple (sortie de la console Chrome) :

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

Nous voyons ici que data.items est un tableau avec deux éléments qui sont tous deux des objets. Dans la console Chrome, les objets peuvent même être développés et inspectés immédiatement.

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

Cela nous dit que data.items[1] est un objet, et après l'avoir développé, nous voyons qu'il a trois propriétés, id , name et __proto__ . Cette dernière est une propriété interne utilisée pour la chaîne de prototypes de l'objet. La chaîne de prototypes et l'héritage sont hors de portée de cette réponse, cependant.

4 votes

Une partie de ce qui est lié ici est vraiment demandé comment faire cela dans jQuery, qui pour être juste simplifie 1 ou 2 choses ici. Je ne sais pas s'il faut en faire un mégapost ou répondre à ces questions séparément - les bases couvertes ici sur ce qu'est un objet et ce qu'est un tableau sont généralement ce qui est vraiment demandé... .

1 votes

@felix-kling Une chose... avec des objets imbriqués, tels que let object = {a: 1, b: 2, c: { a: 3, b: 4 }}; ce qui renvoie un tableau contenant un tableau pour chaque objet imbriqué, dans ce cas-ci [ 1, 2, [ 3, 4 ] ] Ne serait-il pas préférable d'utiliser concat dans l'appel récursif au lieu de push ? (nécessitant que le résultat soit mutable)

2 votes

Cette page m'a permis d'apprendre la différence entre ARRAY et OBJ.

92voto

vitmalina Points 212

Vous pouvez y accéder de cette façon

data.items[1].name

ou

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

Les deux voies sont égales.

7 votes

Le premier est beaucoup plus intuitif, plus lisible et plus court ;) Je préfère utiliser la syntaxe de propriété entre parenthèses uniquement lorsque le nom de la propriété est variable.

36voto

finishingmove Points 7927

Dans le cas où vous essayez d'accéder à un item de la structure de l'exemple par id ou name sans connaître sa position dans le tableau, la façon la plus simple de le faire serait d'utiliser la méthode suivante underscore.js bibliothèque :

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

D'après mon expérience, l'utilisation de fonctions d'ordre supérieur au lieu de for ou for..in Les boucles permettent d'obtenir un code plus facile à raisonner, et donc plus facile à maintenir.

Juste mes deux centimes.

27voto

Travis J Points 28588

Parfois, il peut être souhaitable d'accéder à un objet imbriqué en utilisant une chaîne de caractères. L'approche simple est le premier niveau, par exemple

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

Mais ce n'est souvent pas le cas avec un json complexe. Au fur et à mesure que le json devient plus complexe, les approches pour trouver des valeurs à l'intérieur du json deviennent également complexes. Il est préférable d'adopter une approche récursive pour naviguer dans le json, et la façon dont cette récursion est exploitée dépend du type de données recherchées. Si des instructions conditionnelles sont impliquées, une recherche json peut être un bon outil à utiliser.

Si la propriété à laquelle on accède est déjà connue, mais que le chemin est complexe, par exemple dans cet objet

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

Et vous savez que vous voulez obtenir le premier résultat du tableau dans l'objet, peut-être voudriez-vous utiliser

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

Cependant, cela provoquera une exception car il n'existe aucune propriété d'objet portant ce nom. La solution pour pouvoir l'utiliser serait d'aplatir l'aspect arborescent de l'objet. Cela peut être fait de manière récursive.

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

Maintenant, l'objet complexe peut être aplati

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

Voici un jsFiddle Demo de l'utilisation de cette approche.

0 votes

Pourquoi voudrais-tu utiliser obj["arr[0].name"] au lieu de obj.arr[0].name ? Vous n'avez pas besoin/veut traiter avec objets plats sauf pour la sérialisation.

1 votes

@Bergi - Je vois cette question fréquemment, et comme elle est utilisée de manière canonique, j'ai posté une réponse à cette version de la question. S'il est possible de l'éviter, il est beaucoup plus rapide d'utiliser obj.arr[0].name, mais parfois les gens veulent faire circuler des accesseurs de type chaîne et c'est un exemple de cela.

0 votes

Urgh. Pourtant, il n'y a guère de raison d'aplatir l'objet complet pour n'utiliser qu'un seul chemin de chaîne, vous pourriez simplement l'analyser et faire une recherche dynamique.

9voto

Andrejs Points 4235

Utilisation de JSONPath serait l'une des solutions les plus souples si vous êtes prêt à inclure une bibliothèque : https://github.com/s3u/JSONPath (nœud et navigateur)

Pour votre cas d'utilisation, le chemin json serait :

$..items[1].name

donc :

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

1 votes

Utiliser eval() n'est pas une bonne solution. On peut plutôt utiliser une fonction de première classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X