trier les propriétés de l'objet et JSON.stringify

94

Mon application dispose d'un large éventail d'objets, que je stringifie et que je sauvegarde sur le disque. Malheureusement, lorsque les objets du tableau sont manipulés, et parfois remplacés, les propriétés des objets sont répertoriées dans des ordres différents (leur ordre de création?). Lorsque je fais JSON.stringify () sur le tableau et que je l'enregistre, un diff montre que les propriétés sont répertoriées dans des ordres différents, ce qui est ennuyeux lorsque je tente de fusionner les données davantage avec des outils de diff et de fusion.

Idéalement, je voudrais trier les propriétés des objets par ordre alphabétique avant d'effectuer le stringify, ou dans le cadre de l'opération stringify. Il existe du code pour manipuler les objets du tableau à de nombreux endroits, et les modifier pour toujours créer des propriétés dans un ordre explicite serait difficile.

Les suggestions seraient les bienvenues!

Un exemple condensé:

obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);

La sortie de ces deux appels stringify est différente et apparaît dans un diff de mes données, mais mon application ne se soucie pas de l'ordre des propriétés. Les objets sont construits de nombreuses manières et à différents endroits.

Innovine
la source
Veuillez donner un exemple de l'objet que vous essayez de stringify (sortie JSON ou littéral d'objet JS)
Bojangles
4
Les clés d'objets dans les objets ne sont pas garanties d'avoir un ordre fixe. C'est par conception.
Passant
1
@rab, où avez-vous découvert cela? Remarque: cela pourrait fonctionner (et le fait probablement la plupart du temps), mais je voulais dire que ce n'est pas garanti.
Dogbert
1
OP ici. Il n'y avait aucune dépendance sur l'ordre des propriétés ici, juste une question sur la façon d'éviter les différences dans les données sérialisées. Cela a finalement été résolu en stockant les propriétés dans des tableaux et en les triant avant la sérialisation, un peu comme dans la réponse acceptée ci-dessous.
Innovine

Réponses:

73

L'approche la plus simple, moderne et actuellement prise en charge par le navigateur est simplement la suivante:

JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());

Cependant, cette méthode supprime tous les objets imbriqués qui ne sont pas référencés et ne s'applique pas aux objets dans les tableaux. Vous voudrez également aplatir l'objet de tri si vous voulez quelque chose comme cette sortie:

{"a":{"h":4,"z":3},"b":2,"c":1}

Vous pouvez le faire avec ceci:

var flattenObject = function(ob) {
    var toReturn = {};

    for (var i in ob) {
        if (!ob.hasOwnProperty(i)) continue;

        if ((typeof ob[i]) == 'object') {
            var flatObject = flattenObject(ob[i]);
            for (var x in flatObject) {
                if (!flatObject.hasOwnProperty(x)) continue;

                toReturn[i + '.' + x] = flatObject[x];
            }
        } else {
            toReturn[i] = ob[i];
        }
    }
    return toReturn;
};

JSON.stringify(sortMyObj, Object.keys(flattenObject(sortMyObj)).sort());

Pour le faire par programme avec quelque chose que vous pouvez modifier vous-même, vous devez pousser les noms de propriété de l'objet dans un tableau, puis trier le tableau par ordre alphabétique et parcourir ce tableau (qui sera dans le bon ordre) et sélectionner chaque valeur de l'objet dans cet ordre. "hasOwnProperty" est également coché afin que vous n'ayez définitivement que les propres propriétés de l'objet. Voici un exemple:

var obj = {"a":1,"b":2,"c":3};

function iterateObjectAlphabetically(obj, callback) {
    var arr = [],
        i;

    for (i in obj) {
        if (obj.hasOwnProperty(i)) {
            arr.push(i);
        }
    }

    arr.sort();

    for (i = 0; i < arr.length; i++) {
        var key = obj[arr[i]];
        //console.log( obj[arr[i]] ); //here is the sorted value
        //do what you want with the object property
        if (callback) {
            // callback returns arguments for value, key and original object
            callback(obj[arr[i]], arr[i], obj);
        }
    }
}

iterateObjectAlphabetically(obj, function(val, key, obj) {
    //do something here
});

Encore une fois, cela devrait garantir que vous parcourez dans l'ordre alphabétique.

Enfin, pour aller plus loin pour la manière la plus simple, cette bibliothèque vous permettra de trier récursivement tout JSON que vous lui passez: https://www.npmjs.com/package/json-stable-stringify

var stringify = require('json-stable-stringify');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));

Production

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
marksyzm
la source
9
C'est vraiment ce que j'essayais d'éviter :) Mais merci, cela semble être la solution correcte, bien que lourde.
Innovine
1
Il y a un bogue dans ce code. Vous ne pouvez pas faire référence obj[i]dans la boucle for car il is'agit d'un entier et les noms de propriété ne le sont pas nécessairement (d'où cette question). Ça devrait être obj[arr[i]].
Andrew Ensley
1
Les rappels ne sont pas exclusifs au comportement asynchrone.
marksyzm
2
@Johann En utilisant un rappel ici, il se transforme iterateObjectAlphabeticallyen une fonction réutilisable. Sans le rappel, le «code de la charge utile» devrait être à l'intérieur de la fonction elle-même. Je pense que c'est une solution très élégante et qui est utilisée dans de nombreuses bibliothèques et le noyau JS lui-même. Par exemple, Array.forEach .
Stijn de Witt
1
@marksyzm C'est bon, dans mon cas, je n'ai dû stringifier qu'une sélection de champs. Ainsi, votre réponse m'a mis sur la bonne voie. Merci
Benj
39

Je pense que si vous contrôlez la génération JSON (et cela ressemble à vous), alors pour vos besoins, cela pourrait être une bonne solution: json-stable-stringify

Depuis le site Web du projet:

JSON.stringify () déterministe avec tri personnalisé pour obtenir des hachages déterministes à partir de résultats stringifiés

Si le JSON produit est déterministe, vous devriez pouvoir le comparer / le fusionner facilement.

Stijn de Witt
la source
Notez que ce dépôt est en mauvais état: n'a pas été mis à jour depuis 3 ans et a des problèmes non résolus et des pull requests. Je pense qu'il vaut mieux se pencher sur certaines des nouvelles réponses.
Tom
3
@Tom Notez également que ce repo compte 5 millions de téléchargements hebdomadaires (!). En d'autres termes, il est activement utilisé. Avoir des problèmes ouverts et / ou des demandes d'extraction «non résolus» ne signifie pas grand-chose. Juste que les auteurs ne se soucient pas de ces problèmes ou n'ont pas le temps, etc. Toutes les bibliothèques n'ont pas besoin d'être mises à jour tous les deux mois. En fait, à mon humble avis, les meilleures bibliothèques reçoivent très rarement des mises à jour. Quand quelque chose est fait, c'est fait. Je ne dis pas que ce projet n'a pas de vrais problèmes, mais si c'est le cas, veuillez les signaler. Je n'ai aucune implication avec cette lib BTW.
Stijn de Witt
ES6 je suppose qu'il a cassé le fait de pouvoir trier des objets (non-tableaux), comme spécifié. J'en ai besoin pour l'édition par l'utilisateur. Même si l'ordre n'a pas d'importance pour l'ordinateur. Cela concerne les utilisateurs qui le modifient. Cela valait la peine d'essayer.
TamusJRoyce
En plus de ce que dit @Tom, je soulignerai également que ce projet n'a pas de dépendances d'exécution. Cela signifie que s'il n'y a pas de problèmes de sécurité / maintenance dans ce dépôt, il est probablement assez stable et sûr à utiliser. De plus, je viens de le tester avec ES6 et cela semble fonctionner correctement (au moins dans Firefox).
Phil le
27

Vous pouvez passer un tableau trié des noms de propriétés comme deuxième argument de JSON.stringify():

JSON.stringify(obj, Object.keys(obj).sort())
Christian d'Heureuse
la source
1
Existe-t-il une sorte de garantie documentée sur ce comportement?
riche souvenir du
4
@richremer Oui, dans le standard ECMAScript , le deuxième paramètre de JSON.stringify()est appelé replaceret peut être un tableau. Il est utilisé pour créer un PropertyListqui est ensuite utilisé SerializeJSONObject()pour ajouter les propriétés dans cet ordre. Ceci est documenté depuis ECMAScript 5.
Christian d'Heureuse
14
Notez que si obj contient des objets imbriqués, les clés imbriquées doivent également être présentes dans le deuxième argument; sinon, ils seront abandonnés! Contre-exemple: > JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' Une façon (hacky) de construire la liste de toutes les clés pertinentes est d'utiliser JSON.stringify pour traverser l'objet: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } Exemple:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim
Object.keys () n'est pas récursif. Cela ne fonctionnera pour aucun objet contenant des tableaux ou des objets imbriqués.
Jor
25

Je ne comprends pas pourquoi la complexité des meilleures réponses actuelles est nécessaire, pour obtenir toutes les clés de manière récursive. À moins que des performances parfaites ne soient nécessaires, il me semble que nous pouvons simplement appeler JSON.stringify()deux fois, la première fois pour obtenir toutes les clés, et la deuxième fois, pour vraiment faire le travail. De cette façon, toute la complexité de la récursivité est gérée par stringify, et nous savons qu'il connaît son contenu et comment gérer chaque type d'objet:

function JSONstringifyOrder( obj, space )
{
    var allKeys = [];
    JSON.stringify( obj, function( key, value ){ allKeys.push( key ); return value; } )
    allKeys.sort();
    return JSON.stringify( obj, allKeys, space );
}
Jor
la source
c'est intéressant, mais vous avez en fait "triché" en utilisant stringify pour mapper toutes les clés à un tableau. Ce tableau peut être plus facile, meilleur et plus sûr obtenu via Object.keys
Z. Khullah
5
Non, le fait est que ce Object.keys()n'est pas récursif, donc si vous avez un objet comme {a: "A", b: {c: "C"} }et que vous appelez Object.keys dessus, vous n'obtiendrez que les clés aet b, mais pas c. JSON.stringifysait tout sur la récursivité sur chaque type d'objet géré par le processus de sérialisation JSON, qu'il s'agisse d'un objet ou d'un tableau (et il implémente déjà la logique pour reconnaître les deux), il doit donc tout gérer correctement pour nous.
Jor
1
J'adore cette solution, très élégante et sûre, semble-t-il.
Markus
14

Mise à jour 2018-7-24:

Cette version trie les objets imbriqués et prend également en charge les tableaux:

function sortObjByKey(value) {
  return (typeof value === 'object') ?
    (Array.isArray(value) ?
      value.map(sortObjByKey) :
      Object.keys(value).sort().reduce(
        (o, key) => {
          const v = value[key];
          o[key] = sortObjByKey(v);
          return o;
        }, {})
    ) :
    value;
}


function orderedJsonStringify(obj) {
  return JSON.stringify(sortObjByKey(obj));
}

Cas de test:

  describe('orderedJsonStringify', () => {
    it('make properties in order', () => {
      const obj = {
        name: 'foo',
        arr: [
          { x: 1, y: 2 },
          { y: 4, x: 3 },
        ],
        value: { y: 2, x: 1, },
      };
      expect(orderedJsonStringify(obj))
        .to.equal('{"arr":[{"x":1,"y":2},{"x":3,"y":4}],"name":"foo","value":{"x":1,"y":2}}');
    });

    it('support array', () => {
      const obj = [
        { x: 1, y: 2 },
        { y: 4, x: 3 },
      ];
      expect(orderedJsonStringify(obj))
        .to.equal('[{"x":1,"y":2},{"x":3,"y":4}]');
    });

  });

Réponse obsolète:

Une version concise dans ES2016. Crédit à @codename, de https://stackoverflow.com/a/29622653/94148

function orderedJsonStringify(o) {
  return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}
aleung
la source
La syntaxe n'est pas tout à fait correcte. Devrait être -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Ben
Ceci, pour l'instant, a résolu mon problème avec C # DataContractJsonSerializer et "__type" ne figurant pas en premier dans la chaîne json. Merci.
Yogurt The Wise
2
Notez que cela, comme la réponse de Christian, se comporte mal avec les objets imbriqués.
Daniel Griscom
sortObjPropertiesByKey n'est pas défini. Vouliez-vous utiliser sortObjByKey?
Paul Lynch
Il ne semble pas que cela sortObjByKey()vérifie les références circulaires, donc soyez prudent, il peut se bloquer dans une boucle infinie en fonction des données d'entrée. Exemple: var objA = {}; var objB = {objA: objA}; objA.objB = objB;-> sortObjByKey(objA);->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
Klesun
8

https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490

const replacer = (key, value) =>
    value instanceof Object && !(value instanceof Array) ? 
        Object.keys(value)
        .sort()
        .reduce((sorted, key) => {
            sorted[key] = value[key];
            return sorted 
        }, {}) :
        value;
David Furlong
la source
1
c'est probablement la meilleure réponse là-bas, merci @David
acido
4

C'est la même chose que la réponse de Satpal Singh

function stringifyJSON(obj){
    keys = [];
    if(obj){
        for(var key in obj){
            keys.push(key);
        }
    }
    keys.sort();
    var tObj = {};
    var key;
    for(var index in keys){
        key = keys[index];
        tObj[ key ] = obj[ key ];
    }
    return JSON.stringify(tObj);
}

obj1 = {}; obj1.os="linux"; obj1.name="X";
stringifyJSON(obj1); //returns "{"name":"X","os":"linux"}"

obj2 = {}; obj2.name="X"; obj2.os="linux";
stringifyJSON(obj2); //returns "{"name":"X","os":"linux"}"
Giridhar CR
la source
3

Vous pouvez ajouter une personnalisation toJSON fonction à votre objet que vous pouvez utiliser pour personnaliser la sortie. À l'intérieur de la fonction, l'ajout des propriétés actuelles à un nouvel objet dans un ordre spécifique doit conserver cet ordre lorsqu'il est stringifié.

Vois ici:

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify

Il n'y a pas de méthode intégrée pour contrôler l'ordre, car les données JSON sont censées être accessibles par des clés.

Voici un jsfiddle avec un petit exemple:

http://jsfiddle.net/Eq2Yw/

Essayez de commenter la toJSONfonction - l'ordre des propriétés est inversé. Veuillez noter que cela peut être spécifique au navigateur, c'est-à-dire que la commande n'est pas officiellement prise en charge dans la spécification. Cela fonctionne dans la version actuelle de Firefox, mais si vous voulez une solution 100% robuste, vous devrez peut-être écrire votre propre fonction stringifier.

Éditer:

Voir également cette question SO concernant la sortie non déterministe de stringify, en particulier les détails de Daff sur les différences de navigateur:

Comment vérifier de manière déterministe qu'un objet JSON n'a pas été modifié?

Dave R.
la source
Les propriétés de chaque objet sont connues (et principalement des chaînes), donc coder en dur les propriétés dans mon propre stringifier toJSON pourrait être beaucoup plus rapide que de trier ...!
Innovine
Le 'ordersJsonStringify' ci-dessous fonctionnait presque. Mais cela semble être une meilleure solution pour moi. Quand j'ai besoin d'obtenir '__type' du C # DataContractJsonSerializer pour être le premier élément de la chaîne json. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' etc .....]); un peu de douleur pour avoir la liste de toutes les clés.
Yogurt The Wise
3

Une réponse récursive et simplifiée:

function sortObject(obj) {
    if(typeof obj !== 'object')
        return obj
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]]);       
    return temp;
}

var str = JSON.stringify(sortObject(obj), undefined, 4);
Jason Parham
la source
reorderdevrait être renommé sortObjectet cela ne gère pas les tableaux
Jonathan Gawrych
Merci d'avoir remarqué cela, et le problème d'OP était de recourir à des objets, pas à des tableaux.
Jason Parham
@JonathanGawrych Pourquoi trieriez-vous les tableaux de toute façon, ils sont censés avoir un ordre. Un tableau [1,2,3] n'est pas le même qu'un tableau [2,3,1], mais un objet n'a pas d'ordre des propriétés. Le problème est que l'ordre compte soudainement après la stringification - les chaînes d'objets équivalents à 100% peuvent être différentes. Malheureusement, l'effort pour obtenir un stringify déterministe semble considérable, exemple de package: github.com/substack/json-stable-stringify/blob/master/index.js
Mörre
1
@ Mörre - Je suppose que je n'étais pas assez précis. Par «ne gère pas les tableaux», je voulais dire que les tableaux sont cassés, pas laissés non triés. Le problème est que typeof arr === "object"cela transformerait les tableaux en objets. Par exemple var obj = {foo: ["bar", "baz"]}serait stringifié en{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych
pour résoudre le problème trouvé par @JonathanGawrych:if(obj.constructor.prototype !== Object.prototype)
ricka
3

Vous pouvez trier l'objet par nom de propriété dans EcmaScript 2015

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
Mayki Nayki
la source
Peut-être qu'un meilleur nom serait sortObjectPropertiesByName()ou plus simple sortPropertiesByName().
janvier
3

J'ai pris la réponse de @Jason Parham et j'ai apporté quelques améliorations

function sortObject(obj, arraySorter) {
    if(typeof obj !== 'object')
        return obj
    if (Array.isArray(obj)) {
        if (arraySorter) {
            obj.sort(arraySorter);
        }
        for (var i = 0; i < obj.length; i++) {
            obj[i] = sortObject(obj[i], arraySorter);
        }
        return obj;
    }
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]], arraySorter);       
    return temp;
}

Cela résout le problème de la conversion des tableaux en objets et vous permet également de définir comment trier les tableaux.

Exemple:

var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)

production:

{content:[{id:1},{id:2},{id:3}]}
Peter
la source
2

La réponse acceptée ne fonctionne pas pour moi pour les objets imbriqués pour une raison quelconque. Cela m'a amené à coder le mien. Comme il est fin 2019 lorsque j'écris ceci, il y a quelques autres options disponibles dans la langue.

Mise à jour: Je pense que la réponse de David Furlong est une approche préférable à ma tentative précédente, et je l'ai écartée. Le mien repose sur le support d'Object.entries (...), donc pas de support d'Internet Explorer.

function normalize(sortingFunction) {
  return function(key, value) {
    if (typeof value === 'object' && !Array.isArray(value)) {
      return Object
        .entries(value)
        .sort(sortingFunction || undefined)
        .reduce((acc, entry) => {
          acc[entry[0]] = entry[1];
          return acc;
        }, {});
    }
    return value;
  }
}

JSON.stringify(obj, normalize(), 2);

-

CONSERVER CETTE ANCIENNE VERSION POUR RÉFÉRENCE HISTORIQUE

J'ai trouvé qu'un tableau simple et plat de toutes les clés de l'objet fonctionnera. Dans presque tous les navigateurs (pas Edge ou Internet Explorer, comme on pouvait s'y attendre ) et Node 12+, il existe une solution assez courte maintenant que Array.prototype.flatMap (...) est disponible. (L'équivalent lodash fonctionnerait aussi.) Je n'ai testé que dans Safari, Chrome et Firefox, mais je ne vois aucune raison pour laquelle cela ne fonctionnerait pas ailleurs qui prend en charge flatMap et JSON.stringify standard (...) .

function flattenEntries([key, value]) {
  return (typeof value !== 'object')
    ? [ [ key, value ] ]
    : [ [ key, value ], ...Object.entries(value).flatMap(flattenEntries) ];
}

function sortedStringify(obj, sorter, indent = 2) {
  const allEntries = Object.entries(obj).flatMap(flattenEntries);
  const sorted = allEntries.sort(sorter || undefined).map(entry => entry[0]);
  return JSON.stringify(obj, sorted, indent);
}

Avec cela, vous pouvez stringify sans dépendances tierces et même passer votre propre algorithme de tri qui trie sur les paires d'entrées clé-valeur, de sorte que vous pouvez trier par clé, charge utile ou une combinaison des deux. Fonctionne pour les objets imbriqués, les tableaux et tout mélange d'anciens types de données.

const obj = {
  "c": {
    "z": 4,
    "x": 3,
    "y": [
      2048,
      1999,
      {
        "x": false,
        "g": "help",
        "f": 5
      }
    ]
  },
  "a": 2,
  "b": 1
};

console.log(sortedStringify(obj, null, 2));

Impressions:

{
  "a": 2,
  "b": 1,
  "c": {
    "x": 3,
    "y": [
      2048,
      1999,
      {
        "f": 5,
        "g": "help",
        "x": false
      }
    ],
    "z": 4
  }
}

Si vous devez être compatible avec les moteurs JavaScript plus anciens , vous pouvez utiliser ces versions légèrement plus détaillées qui émulent le comportement de flatMap. Le client doit prendre en charge au moins ES5, donc pas d'Internet Explorer 8 ou inférieur.

Ceux-ci renverront le même résultat que ci-dessus.

function flattenEntries([key, value]) {
  if (typeof value !== 'object') {
    return [ [ key, value ] ];
  }
  const nestedEntries = Object
    .entries(value)
    .map(flattenEntries)
    .reduce((acc, arr) => acc.concat(arr), []);
  nestedEntries.unshift([ key, value ]);
  return nestedEntries;
}

function sortedStringify(obj, sorter, indent = 2) {
  const sortedKeys = Object
    .entries(obj)
    .map(flattenEntries)
    .reduce((acc, arr) => acc.concat(arr), [])
    .sort(sorter || undefined)
    .map(entry => entry[0]);
  return JSON.stringify(obj, sortedKeys, indent);
}
Miles Elam
la source
1

Fonctionne avec lodash, objets imbriqués, toute valeur d'attribut d'objet:

function sort(myObj) {
  var sortedObj = {};
  Object.keys(myObj).sort().forEach(key => {
    sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : myObj[key]
  })
  return sortedObj;
}
JSON.stringify(sort(yourObj), null, 2)

Il repose sur le comportement de Chrome et de Node selon lequel la première clé affectée à un objet est sortie en premier JSON.stringify.

AJP
la source
2
Merci, mais j'ai eu ce problème il y a 4 ans, je suis heureux de dire que j'ai évolué un peu depuis :)
Innovine
1
:) C'est bon de l'entendre. Bien que j'ai eu ce problème hier et que je n'ai pas trouvé la réponse que je cherchais sous votre (ancienne mais toujours pertinente) question :)
AJP
0

Essayer:

function obj(){
  this.name = '';
  this.os = '';
}

a = new obj();
a.name = 'X',
a.os = 'linux';
JSON.stringify(a);
b = new obj();
b.os = 'linux';
b.name = 'X',
JSON.stringify(b);
3y3
la source
Merci, mais apparemment la commande n'est pas définie et ne fonctionne que. Approche intéressante cependant!
Innovine
0

J'ai créé une fonction pour trier l'objet, et avec callback .. qui crée en fait un nouvel objet

function sortObj( obj , callback ) {

    var r = [] ;

    for ( var i in obj ){
        if ( obj.hasOwnProperty( i ) ) {
             r.push( { key: i , value : obj[i] } );
        }
    }

    return r.sort( callback ).reduce( function( obj , n ){
        obj[ n.key ] = n.value ;
        return obj;
    },{});
}

et appelez-le avec un objet.

var obj = {
    name : "anu",
    os : "windows",
    value : 'msio',
};

var result = sortObj( obj , function( a, b ){
    return a.key < b.key  ;    
});

JSON.stringify( result )

qui imprime {"value":"msio","os":"windows","name":"anu"}, et pour le tri avec valeur.

var result = sortObj( obj , function( a, b ){
    return a.value < b.value  ;    
});

JSON.stringify( result )

qui imprime {"os":"windows","value":"msio","name":"anu"}

rab
la source
1
Fonctionne bien, mais malheureusement ce n'est pas récursif.
Jonathan Gawrych
0

Si les objets de la liste n'ont pas les mêmes propriétés, générez un objet maître combiné avant stringify:

let arr=[ <object1>, <object2>, ... ]
let o = {}
for ( let i = 0; i < arr.length; i++ ) {
  Object.assign( o, arr[i] );
}
JSON.stringify( arr, Object.keys( o ).sort() );

Niels Gjeding Olsen
la source
0
function FlatternInSort( obj ) {
    if( typeof obj === 'object' )
    {
        if( obj.constructor === Object )
        {       //here use underscore.js
            let PaireStr = _( obj ).chain().pairs().sortBy( p => p[0] ).map( p => p.map( FlatternInSort ).join( ':' )).value().join( ',' );
            return '{' + PaireStr + '}';
        }
        return '[' + obj.map( FlatternInSort ).join( ',' ) + ']';
    }
    return JSON.stringify( obj );
}

// exemple comme ci-dessous. dans chaque couche, pour des objets comme {}, aplatis dans le tri par clé. pour les tableaux, les nombres ou les chaînes, aplatis comme / avec JSON.stringify.

FlatternInSort ({c: 9, b: {y: 4, z: 2, e: 9}, F: 4, a: [{j: 8, h: 3}, {a: 3, b: 7}] })

"{" F ": 4," a ": [{" h ": 3," j ": 8}, {" a ": 3," b ": 7}]," b ": {" e " : 9, "y": 4, "z": 2}, "c": 9} "

saintthor
la source
Veuillez expliquer pourquoi utiliser une bibliothèque externe, et comme @DeKaNszn l'a dit, ajoutez quelques explications et une introduction. Ce serait apprécié.
Benj
cette fonction est copiée de mon projet. dans lequel j'utilise underscore.js.
saintthor
0

Extension de la réponse d'AJP, pour gérer les tableaux:

function sort(myObj) {
    var sortedObj = {};
    Object.keys(myObj).sort().forEach(key => {
        sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : _.isArray(myObj[key])? myObj[key].map(sort) : myObj[key]
    })
    return sortedObj;
}
gblff
la source
0

Surpris, personne n'a mentionné la isEqualfonction de lodash .

Effectue une comparaison approfondie entre deux valeurs pour déterminer si elles sont équivalentes.

Remarque: Cette méthode prend en charge la comparaison de tableaux, de tampons de tableau, de booléens, d'objets de date, d'objets d'erreur, de mappes, de nombres, d'objets Object, d'expressions régulières, d'ensembles, de chaînes, de symboles et de tableaux typés. Les objets objets sont comparés par leurs propres propriétés énumérables, non héritées. Les fonctions et les nœuds DOM sont comparés par égalité stricte, c'est-à-dire ===.

https://lodash.com/docs/4.17.11#isEqual

Avec le problème d'origine - les clés étant ordonnées de manière incohérente - c'est une excellente solution - et bien sûr, il s'arrêtera simplement s'il trouve un conflit au lieu de sérialiser aveuglément l'objet entier.

Pour éviter d'importer toute la bibliothèque, procédez comme suit:

import { isEqual } from "lodash-es";

Exemple bonus: vous pouvez également l'utiliser avec RxJS avec cet opérateur personnalisé

export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => 
                                                pipe(distinctUntilChanged(isEqual));
Simon_Weaver
la source
0

Après tout, il a besoin d'un tableau qui met en cache toutes les clés de l'objet imbriqué (sinon il omettra les clés non mises en cache.) La réponse la plus ancienne est tout simplement fausse, car le second argument ne se soucie pas de la notation par points. Ainsi, la réponse (en utilisant Set) devient.

function stableStringify (obj) {
  const keys = new Set()
  const getAndSortKeys = (a) => {
    if (a) {
      if (typeof a === 'object' && a.toString() === '[object Object]') {
        Object.keys(a).map((k) => {
          keys.add(k)
          getAndSortKeys(a[k])
        })
      } else if (Array.isArray(a)) {
        a.map((el) => getAndSortKeys(el))
      }
    }
  }
  getAndSortKeys(obj)
  return JSON.stringify(obj, Array.from(keys).sort())
}
Polv
la source
0

Voici une approche de clonage ... clonez l'objet avant de le convertir en json:

function sort(o: any): any {
    if (null === o) return o;
    if (undefined === o) return o;
    if (typeof o !== "object") return o;
    if (Array.isArray(o)) {
        return o.map((item) => sort(item));
    }
    const keys = Object.keys(o).sort();
    const result = <any>{};
    keys.forEach((k) => (result[k] = sort(o[k])));
    return result;
}

Si est très nouveau mais semble fonctionner correctement sur les fichiers package.json.

Corey Alix
la source
-3

Il existe une Array.sortméthode qui peut vous être utile. Par exemple:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});
Egor4eg
la source
1
Je ne souhaite cependant pas trier le tableau. En fait, la partie tableau n'est pas importante .. Je souhaite trier les propriétés d'un objet .. à partir de quelques expériences rapides, il semble que les propriétés sont répertoriées dans l'ordre de leur création. Une façon pourrait être de créer un nouvel objet et de copier les propriétés par ordre alphabétique, mais j'espère qu'il y
aura
1
@Innovine, même cela ne fonctionnerait pas car les clés ne sont pas garanties d'être commandées au moment de la création par la spécification.
Dogbert
Ensuite, la question devient, comment puis-je stringifier mes objets de telle manière que les clés soient dans un ordre fixe ... ce n'est pas impossible pour moi de faire pour (clé dans obj) et de les stocker dans un tableau temporaire et de trier cela et manuellement construire une chaîne .. mais j'espère désespérément qu'il existe un moyen plus simple
Innovine
Non, c'est à peu près la façon de procéder. Bienvenue dans JavaScript.
marksyzm