trier les propriétés des objets et JSON.stringify

mon application a un grand tableau d'objets, que je stringifie et sauve sur le disque. Malheureusement, lorsque les objets du tableau sont manipulés, et parfois remplacés, les propriétés sur les objets sont listées dans des ordres différents (leur ordre de création?). Quand je fais JSON.stringify () sur le tableau et sauvegardez-le, une diff affiche les propriétés qui sont listées dans différents ordres, ce qui est gênant quand on essaye de fusionner les données avec diff et les outils de fusion.

Idéalement, je voudrais trier les propriétés des objets dans l'ordre alphabétique avant de procéder à la stringify, ou dans le cadre de la stringify opération. Il y a du code pour manipuler les objets array dans 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!

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 stringify les appels sont différents, et apparaissent 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 façons et à différents endroits..

55
demandé sur Innovine 2013-04-23 14:57:23

17 réponses

l'approche plus simple, moderne et actuellement prise en charge par le navigateur est simplement celle-ci:

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 aussi 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 programmatiquement avec quelque chose vous pouvez modifier vous-même, vous devez pousser les noms de propriétés de l'objet dans un tableau, puis trier le tableau par ordre alphabétique et itérer par ce tableau (qui sera dans le bon ordre) et sélectionner chaque valeur de l'objet dans cet ordre. "hasOwnProperty" est également coché de sorte que vous avez certainement seulement 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 itérez à travers dans l'ordre alphabétique.

Enfin, en allant plus loin pour la manière la plus simple, cette bibliothèque vous permettra récursivement de trier tout JSON que vous y 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));

Sortie

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
40
répondu marksyzm 2018-09-12 13:07:23

je pense que si vous êtes en contrôle de la génération JSON( et il semble que vous êtes), alors pour vos besoins ce pourrait être une bonne solution: JSON-stable-stringify

du site Web du projet:

deterministic JSON.stringify () avec tri personnalisé pour obtenir déterministe à partir de hachages stringified résultats

si le JSON produit est déterministe vous devriez être capable de le différencier/le fusionner facilement.

26
répondu Stijn de Witt 2014-01-21 19:21:04

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())
14
répondu Christian d'Heureuse 2016-11-17 04:09:19

mise à jour 2018-7-24:

cette version trie les objets imbriqués et supporte le tableau aussi bien:

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 dépréciée:

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), {}));
}
10
répondu aleung 2018-09-06 10:53:14

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"}"
4
répondu Giridhar C R 2013-04-23 13:32:36

Vous pouvez ajouter des toJSON fonction de votre objet que vous pouvez utiliser pour personnaliser la sortie. À l'intérieur de la fonction, l'ajout de propriétés courantes à un nouvel objet dans un ordre spécifique devrait préserver cet ordre lors de la stringification.

voir 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 commande parce que les données JSON sont destinées à être accessibles par des clés.

voici un jsfiddle avec un petit exemple:

http://jsfiddle.net/Eq2Yw/

essayez de commenter la fonction toJSON - 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. Il fonctionne dans la version actuelle de Firefox, mais si vous voulez une solution 100% robuste, vous pouvez avoir à écrire votre propre fonction stringifier.

Edit:

Voir Aussi cette question 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 façon déterministe qu'un objet JSON n'a pas été modifié?

3
répondu Dave R. 2017-05-23 12:26:15

récursive et simplifié réponse:

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);
3
répondu Jason Parham 2015-04-02 13:22:56

j'ai pris la réponse de @Jason Parham et fait 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 corrige le problème des tableaux convertis 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)

sortie:

{content:[{id:1},{id:2},{id:3}]}
3
répondu Peter 2018-01-05 11:02:55

Fonctionne avec lodash, objets imbriqués, de toute valeur d'attribut de l'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 s'appuie sur le comportement de Chrome et de Node pour que la première clé assignée à un objet soit émise en premier par JSON.stringify .

1
répondu AJP 2017-08-09 13:02:10

, Essayez:

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);
0
répondu 3y3 2013-04-23 11:21:31

j'ai créé une fonction pour trier les objets, et avec rappel .. qui créent réellement 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 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"}

0
répondu rab 2013-04-23 12:01:40

vous pouvez trier un 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), {});
}
0
répondu mike 2016-03-05 06:48:10

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;
0
répondu David Furlong 2017-04-26 14:16:37

si les objets de la liste n'ont pas les mêmes propriétés, générer 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() );
0
répondu Niels Gjeding Olsen 2017-05-09 19:08:36
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 ci-dessous. dans chaque couche, pour les objets comme {}, aplatie dans le tri de clés. pour tableaux, nombres ou cordes, 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,"une":[{"h":3,"j":8},{"a":3,"b":7}],"b":{"e":9,"y":4,"z":2},"c":9}"

0
répondu saintthor 2017-08-26 15:32:13

Extension AJP réponse, pour gérer des 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;
}
0
répondu gblff 2018-06-08 10:55:11

il est Array.sort méthode qui peut être utile pour vous. Par exemple:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});
-2
répondu Egor4eg 2017-03-29 17:45:18