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


875

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 ou multiples?

Par exemple:

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

Comment puis-je accéder nameau deuxième élément de items?


22
@Marcel: Il doit être lu comme "J'ai une structure de données imbriquée ou JSON, comment puis-je accéder à une valeur spécifique?". Je connais la différence, mais beaucoup de gens ne le font pas et pourraient rechercher "JSON" plutôt que "objet". De nombreuses questions sont en fait de la forme "comment puis-je accéder à X dans ce JSON". Le seul endroit où je mentionne JSON dans ma réponse est où j'explique ce que c'est. Si vous avez une suggestion pour mieux communiquer cela, je suis à l'écoute.
Felix Kling

duplication possible de JSON en JavaScript
Travis J

Réponses:


1160

Préliminaires

JavaScript n'a qu'un seul type de données qui peut contenir plusieurs valeurs: Objet . Un tableau est une forme spéciale d'objet.

(Ordinaire) Les objets ont la forme

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

Les tableaux ont la forme

[value, value, ...]

Les tableaux et les objets exposent une key -> valuestructure. Les clés d'un tableau doivent être numériques, tandis que n'importe quelle chaîne peut être utilisée comme clé dans des objets. Les paires clé-valeur sont également appelées "propriétés" .

Les propriétés sont accessibles soit en utilisant la notation par points

const value = obj.someProperty;

ou la notation entre crochets , si le nom de la propriété ne serait pas un nom d'identifiant JavaScript valide [spéc] , ou si 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];

Pour cette raison, les éléments du tableau ne sont accessibles qu'en utilisant la notation entre crochets:

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, elles doivent d'abord être converties en types de données JavaScript, c'est-à-dire des tableaux et des objets (et comment travailler avec ceux-ci vient d'être expliqué). Comment analyser JSON est expliqué dans la question Analyser JSON en JavaScript? .

Autres lectures

Comment accéder aux tableaux et aux objets est une connaissance fondamentale de JavaScript et il est donc conseillé de lire le guide JavaScript MDN , 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. De telles structures sont accessibles 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 voulons accéder nameau deuxième élément.

Voici comment nous pouvons le faire étape par étape:

Comme nous pouvons le voir dataest un objet, donc nous pouvons accéder à ses propriétés en utilisant la notation par points. La itemspropriété 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 crochets:

data.items[1]

Cette valeur est un objet et nous utilisons à nouveau la notation par points pour accéder à la namepropriété. Nous obtenons donc finalement:

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

Alternativement, nous aurions pu utiliser la notation entre crochets pour l'une des 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 ne undefinedreviens que?

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

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

Utilisez console.logou console.diret inspectez la structure de l'objet / du tableau. La propriété à laquelle vous essayez d'accéder peut être réellement définie sur un objet / tableau imbriqué.

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

Que faire si les noms de propriété sont dynamiques et que je ne les connais pas au préalable?

Si les noms de propriété 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 boucle for...in [MDN] pour les objets et la boucle for [MDN] pour les tableaux pour 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 l' objet comme ceci:

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
}

Selon d'où vient l'objet (et ce que vous voulez faire), vous devrez peut-être tester à chaque itération si la propriété est vraiment une propriété de l'objet ou une propriété héritée. Vous pouvez le faire avec Object#hasOwnProperty [MDN] .

Comme alternative à for...inwith 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 parcourir tous les éléments du data.items tableau , nous utilisons une forboucle:

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...inpour itérer sur les tableaux, mais il y a des raisons pour lesquelles cela devrait être évité: Pourquoi est-ce que 'pour (élément var dans la liste)' avec des tableaux est considéré comme une mauvaise pratique en JavaScript? .

Avec la prise en charge croissante des navigateurs d'ECMAScript 5, 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 la boucle [MDN] , qui fonctionne non seulement pour les tableaux, mais pour tout élément itérable :for...of

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

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


Et si la "profondeur" de la structure de données m'est inconnue?

En plus des clés inconnues, la "profondeur" de la structure de données (c'est-à-dire le nombre d'objets imbriqués) qu'elle possède 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 exacte des données.

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

Voici un exemple pour obtenir le premier nœud 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);

Un moyen plus générique d'accéder à une structure de données imbriquée avec des clés et une profondeur inconnues consiste à tester le type de la valeur et à agir en conséquence.

Voici un exemple qui ajoute toutes les valeurs primitives à l'intérieur d'une structure de données imbriquée dans un tableau (en supposant qu'elle ne contient aucune fonction). Si nous rencontrons un objet (ou un tableau), nous appelons simplement à toArraynouveau 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;
}



Aides

Étant donné que 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] nous aident à le faire. Par exemple (sortie de la console Chrome):

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

Ici, nous voyons que c'est data.itemsun 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 étendu , nous voyons qu'il a trois propriétés, id, nameet __proto__. Ce dernier est une propriété interne utilisée pour la chaîne prototype de l'objet. La chaîne de prototype et l'héritage sont cependant hors de portée pour cette réponse.


3
Une partie de ce qui est lié ici demande vraiment comment faire cela dans jQuery, ce qui, pour être juste, simplifie 1 ou 2 choses ici. Je ne sais pas s'il faut en faire davantage un mégaposte ou y répondre séparément - les bases abordées ici sur ce qu'est un objet, ce qui est un tableau sont généralement ce qui est vraiment demandé ....
Chris Moschini

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

3
C'est la réponse la plus approfondie que j'ai jamais vue sur Stack Overflow - et elle a répondu à ma question! Merci!
William Jones

cette seule page m'a fait apprendre la différence entre ARRAY et OBJ
4ni5

76

Vous pouvez y accéder de cette façon

data.items[1].name

ou

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

Les deux façons sont égales.


Oui, mais vous ne pouvez pas faire de données ["items"]. 1.name
neaumusic

5
Le premier est beaucoup plus intuitif, lisible et plus court;) Je préfère utiliser la syntaxe des propriétés des crochets uniquement lorsque le nom de la propriété est variable.
DanteTheSmith

35

Dans le cas où vous essayez d'accéder à un à itempartir de la structure d'exemple par idou name, sans connaître sa position dans le tableau, la façon la plus simple de le faire serait d'utiliser la bibliothèque 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"}

D'après mon expérience, l'utilisation de fonctions d'ordre supérieur au lieu de forou de for..inboucles aboutit à un code plus facile à raisonner et donc plus facile à gérer.

Juste mes 2 cents.


29

Les objets et les tableaux disposent de nombreuses méthodes intégrées qui peuvent vous aider à traiter les données.

Remarque: dans de nombreux exemples, j'utilise des fonctions fléchées . Ils sont similaires aux expressions de fonction , mais ils lient la thisvaleur lexicalement.

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

Object.keys()renvoie un tableau de clés d'objet, Object.values()renvoie un tableau de valeurs d'objet et Object.entries()renvoie un tableau de clés d'objet et les valeurs correspondantes dans un format [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() avec une boucle for-of et une affectation destructurante

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

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

Il est très pratique d'itérer le résultat de Object.entries()avec une boucle for-of et une affectation de déstructuration .

La boucle For-of vous permet d'itérer les éléments du tableau. La syntaxe est for (const element of array)(nous pouvons remplacer constpar varou let, mais il vaut mieux utiliser constsi nous n'avons pas l'intention de modifier element).

L'affectation de déstructuration vous permet d'extraire des valeurs d'un tableau ou d'un objet et de les affecter à des variables. Dans ce cas, cela const [key, value]signifie qu'au lieu d'affecter le [key, value]tableau à element, nous attribuons le premier élément de ce tableau à keyet le deuxième élément à value. C'est équivalent à ceci:

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

Comme vous pouvez le voir, la déstructuration rend cela beaucoup plus simple.

Array.prototype.every() et Array.prototype.some()

La every()méthode renvoie truesi la fonction de rappel spécifiée renvoie truepour chaque élément du tableau. La some()méthode renvoie truesi la fonction de rappel spécifiée renvoie truepour certains (au moins un) élément.

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

Les find()méthodes retournent le premier élément qui satisfait la fonction de rappel fournie. La filter()méthode renvoie un tableau de tous les éléments qui satisfait la fonction de rappel fournie.

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

La map()méthode renvoie un tableau avec les résultats de l'appel d'une fonction de rappel fournie sur les éléments du tableau.

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

La reduce()méthode réduit un tableau à une seule valeur en appelant la fonction de rappel fournie avec deux éléments.

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

La reduce()méthode prend un deuxième paramètre facultatif, qui est la valeur initiale. Ceci est utile lorsque le tableau sur lequel vous appelez reduce()peut avoir zéro ou un élément. Par exemple, si nous voulions créer une fonction sum()qui prend un tableau comme argument et retourne la somme de tous les éléments, nous pourrions l'écrire comme ça:

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


Ceci est ma réponse préférée. Vous pouvez également ajouter un exemple de boucle juste une donnée imbriquée spécifique, commeObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
SilverSurfer

25

Parfois, l'accès à un objet imbriqué à l'aide d'une chaîne peut être souhaitable. 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 json complexe. À mesure que json devient plus complexe, les approches pour trouver des valeurs à l'intérieur du json deviennent également complexes. Une approche récursive pour naviguer dans le json est la meilleure, et la manière dont cette récursivité est exploitée dépendra du type de données recherchées. S'il y a des instructions conditionnelles impliquées, une recherche json peut être un bon outil à utiliser.

Si la propriété en cours d'accès 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, vous voudrez peut-être 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 une jsFiddle Demode cette approche utilisée.


Que voudriez-vous utiliser à la obj["arr[0].name"]place de obj.arr[0].name? Vous avez à peine besoin / envie de traiter des objets aplatis, sauf pour la sérialisation.
Bergi

@Bergi - Je vois souvent cette question, et comme elle est utilisée de manière canonique, j'ai posté une réponse à cette version. S'il est évitable, il est beaucoup plus rapide d'utiliser obj.arr [0] .name, mais parfois les gens veulent passer des accesseurs de chaîne et c'est un exemple de le faire.
Travis J

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

14

Cette question est assez ancienne, donc comme une mise à jour contemporaine. Avec le début de ES2015, il existe des alternatives pour obtenir les données dont vous avez besoin. Il existe maintenant une fonctionnalité appelée déstructuration d'objet pour accéder aux objets imbriqués.

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

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

console.log(secondName);

L'exemple ci-dessus crée une variable appelée à secondNamepartir de la nameclé d'un tableau appelé items, le seul ,dit ignorer le premier objet du tableau.

Notamment, il est probablement exagéré pour cet exemple, car l'accès aux tableaux simples est plus facile à lire, mais il s'avère utile lors de la séparation des objets en général.

Il s'agit d'une introduction très brève à votre cas d'utilisation spécifique, la déstructuration peut être une syntaxe inhabituelle à laquelle s'habituer au début. Je vous recommande de lire la documentation de Destructuring Assignment de Mozilla pour en savoir plus.


13

Pour accéder à un attribut imbriqué, vous devez spécifier son nom, puis rechercher dans l'objet.

Si vous connaissez déjà le chemin exact, vous pouvez le coder en dur dans votre script comme ceci:

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

cela fonctionne aussi -

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

Lorsque vous ne connaissez pas le nom exact à l'avance, ou qu'un utilisateur est celui qui vous fournit le nom. Une recherche dynamique dans la structure de données est alors requise. Certains ont suggéré ici que la recherche peut être effectuée à l'aide d'une forboucle, mais il existe un moyen très simple de parcourir un chemin à l'aide de 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)

Le chemin est une façon de dire: Prenez d'abord l'objet avec la clé items, qui se trouve être un tableau. Ensuite, prenez l' 1élément -st (0 tableaux d'index). Prenez en dernier l'objet avec la clé namedans cet élément de tableau, qui se trouve être la chaîne bar.

Si vous avez un très long chemin, vous pourriez même utiliser String.splitpour rendre tout cela plus facile -

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

Il s'agit simplement de JavaScript, sans utiliser de bibliothèques tierces comme jQuery ou 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"

ou

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

Fondamentalement, utilisez un point entre chaque descendant qui se déroule en dessous et lorsque vous avez des noms d'objets composés de deux chaînes, vous devez utiliser la notation ["nom obj"]. Sinon, un simple point suffirait;

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

pour ajouter à cela, l'accès aux tableaux imbriqués se produirait comme suit:

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"

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

Un autre document plus utile décrivant la situation ci-dessus: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation

Accès à la propriété via la marche par points: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation


Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change. - De l'avis
Robert

1
J'ai édité le post. Bien que les gens se soient empressés de lui donner une mauvaise réputation. La prochaine fois, je m'abstiendrai de donner une réponse.
Johnny

1
@Riddick ne s'abstient pas, assurez-vous de ne pas publier uniquement un lien
reggaeguitar

12

Vous pouvez utiliser la lodash _getfonction:

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

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

9

L'utilisation de JSONPath serait l'une des solutions les plus flexibles si vous souhaitez 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
Utiliser eval () n'est pas une bonne solution. À la place, une fonction de première classe peut être utilisée.
pradeep gowda

8

Juste au cas où quelqu'un visiterait cette question en 2017 ou plus tard et chercherait un moyen facile à retenir , voici un article de blog élaboré sur l' accès aux objets imbriqués en JavaScript sans être embrouillé par

Impossible de lire la propriété 'foo' d'une erreur non définie

1. Modèle d'accès aux objets imbriqués d'Oliver Steele

Le moyen le plus simple et le plus propre consiste à utiliser le modèle d'accès aux objets imbriqués d'Oliver Steele

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

Avec cette notation, vous ne rencontrerez jamais

Impossible de lire la propriété 'nom' non définie .

Vous vérifiez essentiellement si l'utilisateur existe, sinon, vous créez un objet vide à la volée. De cette façon, la clé de niveau suivant sera toujours accessible à partir d'un objet qui existe ou d'un objet vide , mais jamais à partir d'un indéfini.

2. Accéder aux objets imbriqués à l'aide de Array Reduce

Pour pouvoir accéder aux tableaux imbriqués, vous pouvez écrire votre propre utilitaire de réduction de tableau.

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.

Il existe également un excellent type de gestion de type de bibliothèque minimale qui fait tout cela pour vous.


3
Cette question concerne principalement les propriétés d'accès existantes. Il y a déjà une question sur ce à quoi vous faites référence (et qui inclut déjà la plupart de vos solutions): accéder aux objets imbriqués Javascript en toute sécurité ou accéder aux objets JavaScript imbriqués avec une clé de chaîne . Mais de toute façon: "Malheureusement, vous ne pouvez pas accéder aux tableaux imbriqués avec cette astuce." Pourquoi pas? Les tableaux sont des objets, cela devrait donc tout aussi bien fonctionner. Pouvez-vous donner un exemple où cela ne fonctionne pas?
Felix Kling

1
@FelixKling Lorsque nous essayons d'accéder à des tableaux avec le modèle d'Oliver Steele, nous ne pouvons pas créer de tableau à la longueur 'n' à la volée et accéder au nième index sans obtenir une erreur 'non définie'. Ex. ((user || {}).address || new Array(3))[1].name
Dinesh Pandiyan

3
Vous n'appliquez pas votre modèle de manière cohérente. Bien sûr, ...[1].barcela entraînerait une erreur si l'élément 1n'existait pas. Mais c'est aussi le cas pour ....foo.barsi foon'existait pas. Vous devez également "garder" l'accès 1, tout comme vous "gardez" tout autre accès à la propriété. Un tableau n'est qu'un objet. Un "élément de tableau" n'est qu'une propriété. Correctement appliqué, il le serait (((user || {}).address || {})[1] || {}).name.
Felix Kling

1
C'est bien. Cela ne m'a pas frappé de cette façon. Merci @FelixKling, je vais mettre à jour les articles du blog.
Dinesh Pandiyan

2
@DineshPandiyan, vous devez divulguer que vous êtes l'auteur de typy, je viens de venir ici après avoir lu votre article de blog
reggaeguitar

8

Je préfère JQuery. C'est plus propre et facile à lire.

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

7

Accès dynamique à un objet à plusieurs niveaux.

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

Violon de travail: https://jsfiddle.net/andreitodorut/3mws3kjL/


6

Si vous recherchez un ou plusieurs objets qui répondent à certains critères, vous avez quelques options en utilisant 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:""});

Il y a aussi un singleet un singleOrDefaultqui fonctionnent comme firstet firstOrDefaultrespectivement. La seule différence est qu'ils lanceront si plus d'une correspondance est trouvée.

pour plus d'explications sur query-js, vous pouvez commencer avec ce post


J'aimerais savoir comment cela pourrait être amélioré. Envie de laisser un commentaire?
Rune FS

6

The Underscore js Way

Qui est une bibliothèque JavaScript qui fournit toute une panoplie d' functional programmingaides utiles sans étendre aucun objet intégré.

Solution:

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

Vieille question mais comme personne n'a mentionné lodash (juste souligner).

Dans le cas où vous utilisez déjà lodash dans votre projet, je pense qu'une manière élégante de le faire dans un exemple complexe:

Opt 1

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

pareil que:

Opt 2

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

La différence entre la première et la deuxième option est que dans l' Opt 1 si vous avez une des propriétés manquantes (non définies) dans le chemin, vous n'obtenez pas d'erreur, cela vous renvoie le troisième paramètre.

Pour le filtre de tableau, lodash a _.find()mais je préfère utiliser le régulier filter(). Mais je pense toujours que la méthode ci-dessus _.get()est super utile lorsque vous travaillez avec des données vraiment complexes. J'étais confronté dans le passé à des API très complexes et c'était pratique!

J'espère que cela peut être utile pour ceux qui recherchent des options pour manipuler des données vraiment complexes que le titre implique.


5

Je ne pense pas que le questionneur concerne uniquement un objet imbriqué de niveau, je présente donc la démo suivante pour montrer comment accéder au nœud de l'objet json profondément imbriqué. Très bien, trouvons le nœud avec l'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>


Comment accéder à un objet json imbriqué à l'aide de variables. data = {a: {b: 'ss'}}; var key = ab data [key] ne fonctionne pas
Pasupathi Rajamanickam

3

Vous pouvez utiliser la syntaxe jsonObject.keypour accéder à la valeur. Et si vous souhaitez accéder à une valeur à partir d'un tableau, vous pouvez utiliser la syntaxe jsonObjectArray[index].key.

Voici les exemples de code pour accéder à différentes valeurs pour vous donner l'idée.

        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

Approche dynamique

Dans la deep(data,key)fonction ci-dessous , vous pouvez utiliser une keychaîne arbitraire - dans votre cas items[1].name(vous pouvez utiliser la notation de tableau [i]à n'importe quel niveau) - si la clé n'est pas valide, undefined est retourné.


2

Une approche pythonique, récursive et fonctionnelle pour démêler des arbres JSON arbitraires:

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)

data est une liste python (analysée à partir d'une chaîne de texte 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
Cette question concerne JavaScript, pas Python. Je ne sais pas s'il y a une question équivalente pour Python.
Felix Kling

2

La fonction grep de jQuery vous permet de filtrer à travers un tableau:

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

Mon stringdatavient d'un fichier PHP mais quand même, je l'indique ici var. Quand je prends directement mon json, objcela ne montrera rien, c'est pourquoi je mets mon fichier json en tant que

var obj=JSON.parse(stringdata); donc après que je reçois messageobj et que je montre dans la boîte d'alerte alors je reçois dataqui est le tableau json et le stocke dans une variable ArrObjpuis je lis le premier objet de ce tableau avec une valeur clé comme celle-ciArrObj[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'exemple est déroutant car il stringjsonne s'agit pas d'une chaîne.
Felix Kling

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.