Question Accès / processus (imbriqués) objets, tableaux ou JSON


J'ai une structure de données imbriquée contenant des objets et des tableaux. Comment puis-je extraire l'information, 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 à name du deuxième article items?


636
2017-08-12 13:02


origine


Réponses:


Préliminaires

JavaScript n'a qu'un seul type de données pouvant contenir plusieurs valeurs: Objet. Un Array est une forme particulière d'objet.

(Plain) Les objets ont la forme

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

Les tableaux ont la forme

[value, value, ...]

Les tableaux et les objets exposent key -> value structure. Les clés d'un tableau doivent être numériques, tandis que toute chaîne peut être utilisée comme clé dans les objets. Les paires clé-valeur sont également appelées "Propriétés".

Les propriétés peuvent être consultées soit en utilisant notation par points

const value = obj.someProperty;

ou notation de support, si le nom de la propriété ne serait pas un 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];

Pour cette raison, les éléments du tableau ne peuvent être accédés qu'en utilisant la notation de parenthèse:

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 doit d'abord être converti en types de données JavaScript, c'est-à-dire des tableaux et des objets (et la façon de travailler avec ceux-ci a été expliquée). Comment analyser JSON est expliqué dans la question Parse JSON en JavaScript? .

Autres documents de lecture

Comment accéder à des tableaux et des objets est une connaissance fondamentale de JavaScript et donc il est conseillé de lire le MDN JavaScript Guide, 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 peuvent être accédées en appliquant consécutivement la notation de point ou de parenthèse.

Voici un exemple:

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

Supposons que nous voulons accéder à la name du deuxième article.

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

Comme on peut le voir data est un objet, donc nous pouvons accéder à ses propriétés en utilisant la notation par points. le items la proprié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 de parenthèse:

data.items[1]

Cette valeur est un objet et nous utilisons à nouveau la notation par points pour accéder au name propriété. Donc nous obtenons finalement:

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

Alternativement, nous aurions pu utiliser la notation bracket pour n'importe laquelle des propriétés, en particulier 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 reçois seulement undefined arrière?

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

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

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

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

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

Si les noms de propriétés sont inconnus ou que nous voulons accéder à toutes les propriétés d'un objet / éléments d'un tableau, nous pouvons utiliser le for...in  [MDN] boucle pour les objets et la for  [MDN] boucle 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 le objet ainsi:

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 l'origine de l'objet (et ce que vous voulez faire), vous devrez peut-être tester à chaque itération si la propriété est réellement une propriété de l'objet, ou s'il s'agit d'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 des noms de propriété:

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

Tableaux

Itérer sur tous les éléments du 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 aussi utiliser for...in itérer sur les tableaux, mais il y a des raisons pour lesquelles cela devrait être évité: Pourquoi 'for (var item in list)' avec des tableaux est-il considéré comme une mauvaise pratique en JavaScript?.

Avec la prise en charge croissante du navigateur ECMAScript 5, la méthode de tableau forEach  [MDN] devient aussi 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 for...of  [MDN] boucle, qui ne fonctionne pas seulement pour les tableaux, mais pour tout 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ératif, il n'y a pas "index" pour accéder ou utiliser.


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

En plus des clés inconnues, la "profondeur" de la structure de données (c'est-à-dire le nombre d'objets imbriqués) dont elle dispose peut également être inconnue. L'accès 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 ex. la représentation d'un arbre binaire, la solution comprend généralement récursivement  [Wikipédia] accéder à 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);

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 façon plus générique d'accéder à une structure de données imbriquée avec des clés et une profondeur inconnues est de tester le type de la valeur et d'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'il ne contient aucune fonction). Si nous rencontrons un objet (ou tableau), nous appelons simplement toArray encore 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));



Assistants

Puisque la structure d'un objet complexe ou d'un tableau 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 à faire cela. Par exemple (sortie de la console Chrome):

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

Ici nous voyons que data.items est un tableau avec deux éléments qui sont tous les 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 élargi, nous voyons qu'il a trois propriétés, id, name et __proto__. Ce dernier est une propriété interne utilisée pour la chaîne prototype de l'objet. Cependant, la chaîne de prototypes et l'héritage sont hors de portée pour cette réponse.


878
2018-05-14 03:29



Vous pouvez y accéder de cette façon

data.items[1].name

ou

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

Les deux moyens sont égaux.


48
2018-06-29 09:15



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

De mon expérience, en utilisant des fonctions d'ordre supérieur au lieu de for ou for..in les boucles donnent un code plus facile à raisonner, et donc plus facile à maintenir.

Juste mes 2 cents.


22
2017-08-18 19:20



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 un json complexe. Comme 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 façon dont cette récursion est exploitée dépendra du type de données recherchées. S'il y a des déclarations conditionnelles, un json recherche 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, peut-être que vous souhaitez utiliser

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

Cependant, cela provoquera une exception car il n'y a pas de propriété d'objet avec ce nom. La solution pour pouvoir utiliser ceci 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 Demo de cette approche étant utilisée.


15
2017-10-22 18:38



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

Note: dans beaucoup des exemples que j'utilise fonctions de flèche. Ils sont similaires à expressions de fonctionmais ils lient le this valeur lexicale.

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

Object.keys() retourne un tableau des clés de l'objet, Object.values() retourne un tableau de valeurs d'objet, et Object.entries() retourne un tableau des clés de l'objet et des 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 de for-of et de déstructuration

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 un pour-de la boucle et affectation destructuring.

For-of loop vous permet d'itérer les éléments du tableau. La syntaxe est for (const element of array) (nous pouvons remplacer const avec var ou letmais il vaut mieux utiliser const si nous n'avons pas l'intention de modifier element).

L'affectation destructive vous permet d'extraire des valeurs d'un tableau ou d'un objet et de les affecter à des variables. Dans ce cas const [key, value] signifie qu'au lieu d'attribuer le [key, value] tableau à element, nous affectons le premier élément de ce tableau à key et 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()

le every() retour de méthode truesi la fonction de rappel spécifiée retourne true pour chaque élément du tableau. le some() retour de méthode truesi la fonction de rappel spécifiée retourne true pour 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()

le find() retours de méthodes la première élément qui satisfait la fonction de rappel fournie. le filter() méthode renvoie un tableau de tout éléments qui satisfont 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()

le map() La 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()

le reduce() La 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

le reduce() La méthode prend un second 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éments. Par exemple, si nous voulions créer une fonction sum() qui prend un tableau en argument et retourne la somme de tous les éléments, on pourrait 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


11
2018-05-06 10:45



Cette question est assez ancienne, donc une mise à jour contemporaine. Avec le début d'ES2015, il existe des alternatives pour obtenir les données dont vous avez besoin. Il y a maintenant une fonctionnalité appelée déstructuration d'objets 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 secondName du name clé d'un tableau appelé items, le solitaire , dit ignorer le premier objet dans le tableau.

Notamment, il est probablement exagéré pour cet exemple, car le simple accès au tableau est plus facile à lire, mais il est utile lorsqu'il s'agit de casser des objets en général.

Ceci est une brève introduction à votre cas d'utilisation spécifique, la déstructuration peut être une syntaxe inhabituelle pour s'y habituer au début. Je recommande de lire Documentation sur l'affectation de Mozilla à la déstructuration pour apprendre plus.


8
2018-06-26 12:43



En utilisant JSONPath serait l'une des solutions les plus flexibles si vous souhaitez inclure une bibliothèque: https://github.com/s3u/JSONPath (noeud et navigateur)

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

$..items[1].name

alors:

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

6
2018-02-24 15:55



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

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


4
2018-06-17 12:16