J'ai jeté du code ensemble pour aplatir et non aplatir les objets JSON complexes / imbriqués. Cela fonctionne, mais c'est un peu lent (déclenche l'avertissement «long script»).
Pour les noms aplatis que je veux "." comme délimiteur et [INDEX] pour les tableaux.
Exemples:
un-flattened | flattened
---------------------------
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
[1,[2,[3,4],5],6] => {"[0]":1,"[1].[0]":2,"[1].[1].[0]":3,"[1].[1].[1]":4,"[1].[2]":5,"[2]":6}
J'ai créé un benchmark qui ~ simule mon cas d'utilisation http://jsfiddle.net/WSzec/
- Obtenir un objet JSON imbriqué
- Aplatir
- Regardez à travers et modifiez-le éventuellement lorsqu'il est aplati
- Redressez-le à son format imbriqué d'origine pour qu'il soit expédié
Je voudrais un code plus rapide: pour clarification, un code qui complète le benchmark JSFiddle ( http://jsfiddle.net/WSzec/ ) beaucoup plus rapide (~ 20% + serait bien) dans IE 9+, FF 24+ et Chrome 29 +.
Voici le code JavaScript pertinent: Current Fastest: http://jsfiddle.net/WSzec/6/
JSON.unflatten = function(data) {
"use strict";
if (Object(data) !== data || Array.isArray(data))
return data;
var result = {}, cur, prop, idx, last, temp;
for(var p in data) {
cur = result, prop = "", last = 0;
do {
idx = p.indexOf(".", last);
temp = p.substring(last, idx !== -1 ? idx : undefined);
cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : {}));
prop = temp;
last = idx + 1;
} while(idx >= 0);
cur[prop] = data[p];
}
return result[""];
}
JSON.flatten = function(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop ? prop+"."+i : ""+i);
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
EDIT 1 Modification de la mise en œuvre de @Bergi qui est actuellement la plus rapide. En passant, l'utilisation de ".indexOf" au lieu de "regex.exec" est environ 20% plus rapide dans FF mais 20% plus lente dans Chrome; je vais donc m'en tenir à l'expression régulière car elle est plus simple (voici ma tentative d'utiliser indexOf pour remplacer l'expression régulière http://jsfiddle.net/WSzec/2/ ).
EDIT 2 En me basant sur l'idée de @Bergi, j'ai réussi à créer une version non-regex plus rapide (3x plus rapide dans FF et ~ 10% plus rapide dans Chrome). http://jsfiddle.net/WSzec/6/ Dans l'implémentation this (l'actuelle), les règles pour les noms de clé sont simplement, les clés ne peuvent pas commencer par un entier ou contenir un point.
Exemple:
- {"foo": {"bar": [0]}} => {"foo.bar.0": 0}
EDIT 3 L'ajout de l'approche d'analyse de chemin en ligne de @AaditMShah (plutôt que String.split) a contribué à améliorer les performances de mise à plat. Je suis très content de l'amélioration globale des performances atteinte.
Les derniers jsfiddle et jsperf:
la source
[1].[1].[0]
me semble faux. Êtes-vous sûr que c'est le résultat souhaité?Réponses:
Voici ma mise en œuvre beaucoup plus courte:
flatten
n'a pas beaucoup changé (et je ne sais pas si vous avez vraiment besoin de cesisEmpty
cas):Ensemble, ils exécutent votre benchmark environ la moitié du temps (Opera 12.16: ~ 900ms au lieu de ~ 1900ms, Chrome 29: ~ 800ms au lieu de ~ 1600ms).
Remarque: cette solution et la plupart des autres solutions traitées ici se concentrent sur la vitesse et sont sensibles à la pollution des prototypes et ne doivent pas être utilisées sur des objets non fiables.
la source
result === data
cela ne fonctionnera pas, ils ne sont jamais identiques.J'ai écrit deux fonctions
flatten
etunflatten
un objet JSON.Aplatir un objet JSON :
Performance :
Annuler l'aplatissement d'un objet JSON :
Performance :
Aplatir et aplatir un objet JSON :
Dans l'ensemble, ma solution fonctionne aussi bien ou même mieux que la solution actuelle.
Performance :
Format de sortie :
Un objet aplati utilise la notation par points pour les propriétés d'objet et la notation entre crochets pour les indices de tableau:
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
[1,[2,[3,4],5],6] => {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}
À mon avis, ce format est meilleur que d'utiliser uniquement la notation par points:
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a.0.b.0":"c","a.0.b.1":"d"}
[1,[2,[3,4],5],6] => {"0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6}
Avantages :
Inconvénients :
La démo actuelle de JSFiddle a donné les valeurs suivantes en sortie:
Ma démo JSFiddle mise à jour a donné les valeurs suivantes en sortie:
Je ne suis pas vraiment sûr de ce que cela signifie, donc je vais m'en tenir aux résultats jsPerf. Après tout, jsPerf est un utilitaire d'analyse comparative des performances. JSFiddle ne l'est pas.
la source
unflatten({"foo.__proto__.bar": 42})
3 ans et demi plus tard ...
Pour mon propre projet, je voulais aplatir les objets JSON en notation par points mongoDB et j'ai trouvé une solution simple:
Caractéristiques et / ou mises en garde
{a: () => {}}
vous pourriez ne pas obtenir ce que vous vouliez!{a: {}, b: []}
c'est aplati{}
.la source
{"x": "abc\"{x}\"yz"}
devient{ "x": "abc"{,"x",}"yz"}
ce qui est invalide.Version ES6:
Exemple:
la source
Date
, aucune idée de comment le faire faire? Par exemple, avecflatten({a: {b: new Date()}});
Voici une autre approche qui fonctionne plus lentement (environ 1000 ms) que la réponse ci-dessus, mais qui a une idée intéressante :-)
Au lieu d'itérer dans chaque chaîne de propriétés, il sélectionne simplement la dernière propriété et utilise une table de consultation pour le reste pour stocker les résultats intermédiaires. Cette table de recherche sera itérée jusqu'à ce qu'il n'y ait plus de chaînes de propriétés et que toutes les valeurs résident sur des propriétés non cataloguées.
Il utilise actuellement le
data
paramètre d'entrée de la table et y met de nombreuses propriétés - une version non destructive devrait également être possible. Peut-être qu'unelastIndexOf
utilisation intelligente fonctionne mieux que le regex (dépend du moteur de regex).Voyez-le en action ici .
la source
unflatten
correctement à l'objet aplati. Par exemple, considérez le tableau[1,[2,[3,4],5],6]
. Votreflatten
fonction aplatit cet objet en{"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}
.unflatten
Cependant, votre fonction n'aplatit pas correctement l'objet aplati[1,[null,[3,4]],6]
. La raison pour laquelle cela se produit est à cause de l'instructiondelete data[p]
qui supprime prématurément la valeur intermédiaire[2,null,5]
avant d'[3,4]
y être ajoutée. Utilisez une pile pour le résoudre. :-)Vous pouvez utiliser https://github.com/hughsk/flat
Exemple de la doc
la source
Ce code aplatit de manière récursive les objets JSON.
J'ai inclus mon mécanisme de chronométrage dans le code et cela me donne 1 ms mais je ne suis pas sûr que ce soit le plus précis.
Production:
la source
typeof some === 'object'
c'est plus rapidesome instanceof Object
puisque le premier contrôle s'effectue en O1 tandis que le second en On où n est une longueur d'une chaîne d'héritage (Object sera toujours le dernier).J'ai ajouté une efficacité de +/- 10 à 15% à la réponse sélectionnée en refactorisant le code mineur et en déplaçant la fonction récursive en dehors de l'espace de noms de la fonction.
Voir ma question: les fonctions d'espacement de noms sont-elles réévaluées à chaque appel? pourquoi cela ralentit les fonctions imbriquées.
Voir référence .
la source
Voici la mienne. Il s'exécute en moins de 2 ms dans Google Apps Script sur un objet de grande taille. Il utilise des tirets au lieu de points pour les séparateurs, et il ne gère pas les tableaux spécialement comme dans la question du demandeur, mais c'est ce que je voulais pour mon utilisation.
Exemple:
Exemple de sortie:
la source
Utilisez cette bibliothèque:
Utilisation (depuis https://www.npmjs.com/package/flat ):
Aplatir:
Désaplatir:
la source
Je voudrais ajouter une nouvelle version de flatten case (c'est ce dont j'avais besoin :)) qui, selon mes sondes avec le jsFiddler ci-dessus, est légèrement plus rapide que celle actuellement sélectionnée. De plus, je vois personnellement cet extrait de code un peu plus lisible, ce qui est bien sûr important pour les projets multi-développeurs.
la source
Voici un code que j'ai écrit pour aplatir un objet avec lequel je travaillais. Il crée une nouvelle classe qui prend chaque champ imbriqué et l'amène dans la première couche. Vous pouvez le modifier pour l'aplatir en vous rappelant l'emplacement d'origine des clés. Il suppose également que les clés sont uniques, même entre les objets imbriqués. J'espère que ça aide.
À titre d'exemple, il convertit
dans
la source