ordinare le proprietà degli oggetti e JSON.stringify


94

La mia applicazione ha una vasta gamma di oggetti, che li stringa e li salvo su disco. Sfortunatamente, quando gli oggetti nell'array vengono manipolati e talvolta sostituiti, le proprietà degli oggetti vengono elencate in diversi ordini (il loro ordine di creazione?). Quando eseguo JSON.stringify () sull'array e lo salvo, un diff mostra le proprietà che vengono elencate in ordini diversi, il che è fastidioso quando si tenta di unire ulteriormente i dati con diff e strumenti di fusione.

Idealmente, vorrei ordinare le proprietà degli oggetti in ordine alfabetico prima di eseguire la stringa, o come parte dell'operazione di stringa. C'è del codice per manipolare gli oggetti dell'array in molti posti e alterarli per creare sempre proprietà in un ordine esplicito sarebbe difficile.

I suggerimenti sarebbero i benvenuti!

Un esempio condensato:

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

L'output di queste due chiamate stringify è diverso e viene visualizzato in un diff dei miei dati, ma la mia applicazione non si preoccupa dell'ordinamento delle proprietà. Gli oggetti sono costruiti in molti modi e luoghi.


Fornisci un esempio dell'oggetto che stai cercando di stringere (output JSON o valore letterale dell'oggetto JS)
Bojangles

4
Non è garantito che le chiavi degli oggetti negli oggetti abbiano un ordine fisso. Questo è in base alla progettazione.
Passante


1
@rab, dove l'hai scoperto? Nota: potrebbe funzionare (e probabilmente funziona la maggior parte del tempo), ma volevo dire che non è garantito.
Dogbert

1
OP qui. Non c'era dipendenza dall'ordine delle proprietà qui, solo una domanda su come evitare le differenze nei dati serializzati. Alla fine questo problema è stato risolto nascondendo le proprietà negli array e ordinandole prima della serializzazione, proprio come nella risposta accettata di seguito.
Innovine

Risposte:


73

L'approccio più semplice, moderno e attualmente supportato dal browser è semplicemente questo:

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

Tuttavia, questo metodo rimuove gli oggetti nidificati a cui non si fa riferimento e non si applica agli oggetti all'interno di array. Dovrai appiattire anche l'oggetto di ordinamento se vuoi qualcosa di simile a questo output:

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

Puoi farlo con questo:

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

Per farlo in modo programmatico con qualcosa che puoi modificare da solo, devi inserire i nomi delle proprietà dell'oggetto in un array, quindi ordinare l'array alfabeticamente e iterare attraverso quell'array (che sarà nell'ordine giusto) e selezionare ogni valore dall'oggetto in quell'ordine. Anche "hasOwnProperty" è selezionato in modo da avere sicuramente solo le proprietà dell'oggetto. Ecco un esempio:

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

Anche in questo caso, questo dovrebbe garantire che si itera in ordine alfabetico.

Infine, portandola oltre per il modo più semplice, questa libreria ti consentirà in modo ricorsivo di ordinare qualsiasi JSON che ci passi: 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));

Produzione

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}

9
Questo è quello che stavo cercando di evitare :) Ma grazie, sembra la soluzione corretta, anche se pesante.
Innovine

1
C'è un bug in questo codice. Non è possibile fare riferimento obj[i]nel ciclo for perché iè un numero intero e i nomi delle proprietà non lo sono necessariamente (da qui questa domanda). Dovrebbe essere obj[arr[i]].
Andrew Ensley

1
I callback non sono esclusivi del comportamento asincrono.
marksyzm

2
@ Johann Usando un callback qui, si trasforma iterateObjectAlphabeticallyin una funzione riutilizzabile. Senza il callback il "codice del payload" dovrebbe essere all'interno della funzione stessa. Penso che sia una soluzione molto elegante e che viene utilizzata in molte librerie e nel core JS stesso. Ad esempio, Array.forEach .
Stijn de Witt

1
@marksyzm Va bene, nel mio caso ho dovuto stringere solo una selezione di campi. Quindi, la tua risposta mi ha messo in cammino. Grazie
Benj

39

Penso che se hai il controllo della generazione JSON (e sembra che tu lo sia), allora per i tuoi scopi questa potrebbe essere una buona soluzione: json-stable-stringify

Dal sito web del progetto:

JSON.stringify () deterministico con ordinamento personalizzato per ottenere hash deterministici da risultati stringa

Se il JSON prodotto è deterministico dovresti essere in grado di diffonderlo / unirlo facilmente.


Tieni presente che quel repo è in cattive condizioni: non è stato aggiornato da 3 anni e presenta problemi irrisolti e richieste pull. Penso che sia meglio guardare ad alcune delle risposte più recenti.
Tom

3
@ Tom Nota anche che quel repo ha 5 milioni di download settimanali (!). In altre parole, viene utilizzato attivamente. Avere problemi aperti "irrisolti" e / o richieste pull non significa molto. Solo che gli autori non si preoccupano di questi problemi o non hanno tempo, ecc. Non tutte le librerie devono essere aggiornate ogni due mesi. In effetti, imho, le migliori librerie ricevono aggiornamenti molto raramente. Quando qualcosa è fatto, è fatto. Non sto dicendo che questo progetto non abbia problemi reali, ma se è così, ti preghiamo di segnalarli. Non ho alcun coinvolgimento con questa libreria BTW.
Stijn de Witt,

ES6 Immagino che non sia riuscito a ordinare gli oggetti (non array), come specificato. Ne ho bisogno per la modifica dell'utente. Anche se l'ordine non ha importanza per il computer. Lo fa per gli utenti che lo modificano. Valeva la pena provare.
TamusJRoyce

Oltre a ciò che dice @Tom, sottolineerò anche che questo progetto non ha dipendenze di runtime. Significa che se non ci sono problemi di sicurezza / manutenzione in questo repository, allora è probabilmente abbastanza stabile e sicuro da usare. Inoltre, l'ho appena testato con ES6 e sembra funzionare bene (almeno in Firefox).
Phil

27

È possibile passare un array ordinato dei nomi delle proprietà come secondo argomento di JSON.stringify():

JSON.stringify(obj, Object.keys(obj).sort())

1
Esiste una sorta di garanzia documentata su questo comportamento?
Rich Remer

4
@richremer Sì, nello standard ECMAScript , il secondo parametro di JSON.stringify()viene chiamato replacere può essere un array. Viene utilizzato per creare un file PropertyListche viene poi utilizzato SerializeJSONObject()per aggiungere le proprietà in quell'ordine. Questo è documentato da ECMAScript 5.
Christian d'Heureuse

14
Si noti che se obj contiene oggetti annidati, le chiavi annidate devono essere presenti anche nel secondo argomento; in caso contrario, verranno eliminati! Controesempio: > JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' un modo (hacky) per costruire l'elenco di tutte le chiavi rilevanti è usare JSON.stringify per attraversare l'oggetto: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } Esempio:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim

Object.keys () non è ricorsivo. Ciò non funzionerà per alcun oggetto contenente array o oggetti nidificati.
Jor

25

Non capisco perché sia ​​necessaria la complessità delle attuali migliori risposte, per ottenere tutte le chiavi in ​​modo ricorsivo. A meno che non sia necessaria una prestazione perfetta, mi sembra che possiamo semplicemente chiamare JSON.stringify()due volte, la prima volta per ottenere tutte le chiavi e la seconda volta, per fare davvero il lavoro. In questo modo, tutta la complessità della ricorsione viene gestita stringify, e sappiamo che sa il fatto suo e come gestire ogni tipo di oggetto:

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

questo è interessante, ma in realtà hai "imbrogliato" usando stringify per mappare tutte le chiavi su un array. Quell'array può essere più facile, migliore e più sicuro ottenuto tramite Object.keys
Z. Khullah

5
No, il punto è che Object.keys()non è ricorsivo, quindi se hai un oggetto come {a: "A", b: {c: "C"} }e chiami Object.keys su di esso, otterrai solo le chiavi ae b, ma non c. JSON.stringifysa tutto sulla ricorsione su ogni tipo di oggetto gestito dal processo di serializzazione JSON, sia esso un oggetto o un array (e implementa già la logica per riconoscerli entrambi), quindi dovrebbe gestire tutto correttamente per noi.
Jor

1
Adoro questa soluzione, molto elegante e sicura a quanto pare.
Markus

14

Aggiornamento 2018-7-24:

Questa versione ordina gli oggetti annidati e supporta anche gli array:

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

Scenario di 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}]');
    });

  });

Risposta obsoleta:

Una versione concisa in ES2016. Ringraziamo @codename, da https://stackoverflow.com/a/29622653/94148

function orderedJsonStringify(o) {
  return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}

La sintassi non è del tutto corretta. Dovrebbe essere -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Ben

Questo, per ora, ha risolto il mio problema con C # DataContractJsonSerializer e "__type" non elencati per primi nella stringa json. Grazie.
Yogurt The Wise

2
Nota che questo, come la risposta di Christian, si comporta male con gli oggetti annidati.
Daniel Griscom

sortObjPropertiesByKey non è definito. Volevi usare sortObjByKey?
Paul Lynch

Non sembra che questo sortObjByKey()controlli i riferimenti circolari, quindi fai attenzione, potrebbe bloccarsi in un ciclo infinito a seconda dei dati di input. Esempio: var objA = {}; var objB = {objA: objA}; objA.objB = objB;-> sortObjByKey(objA);->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
Klesun


4

Questa è la stessa della risposta di 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"}"

3

Puoi aggiungere una toJSONfunzione personalizzata al tuo oggetto che puoi usare per personalizzare l'output. All'interno della funzione, l'aggiunta di proprietà correnti a un nuovo oggetto in un ordine specifico dovrebbe preservare quell'ordine quando è stringa.

Vedere qui:

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

Non esiste un metodo integrato per il controllo dell'ordinamento perché è possibile accedere ai dati JSON tramite chiavi.

Ecco un jsfiddle con un piccolo esempio:

http://jsfiddle.net/Eq2Yw/

Prova a commentare la toJSONfunzione: l'ordine delle proprietà è invertito. Tieni presente che questo può essere specifico del browser, ovvero l'ordinazione non è ufficialmente supportata nelle specifiche. Funziona nella versione corrente di Firefox, ma se desideri una soluzione robusta al 100%, potresti dover scrivere la tua funzione di stringa.

Modificare:

Vedi anche questa domanda SO riguardante l'output non deterministico di stringify, in particolare i dettagli di Daff sulle differenze del browser:

Come verificare deterministicamente che un oggetto JSON non sia stato modificato?


Le proprietà di ogni oggetto sono note (e principalmente stringhe), quindi codificare le proprietà nel mio stringifier toJSON potrebbe essere molto più veloce dell'ordinamento ...!
Innovine

"OrderJsonStringify" di seguito ha quasi funzionato. Ma questa sembra essere una soluzione migliore per me. Quando ho bisogno di ottenere '__type' di C # DataContractJsonSerializer come primo elemento nella stringa json. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' ecc .....]); un po 'di dolore per elencare tutte le chiavi.
Yogurt The Wise

3

Una risposta ricorsiva e semplificata:

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

reorderdovrebbe essere rinominato in sortObjecte questo non gestisce gli array
Jonathan Gawrych

Grazie per averlo notato, e il problema di OP era ricorrere a oggetti, non array.
Jason Parham

@JonathanGawrych Perché dovresti ordinare gli array comunque, dovrebbero avere un ordine. Un array [1,2,3] non è lo stesso di un array [2,3,1], ma un oggetto non ha un ordine di proprietà. Il problema è che l'ordine diventa improvvisamente importante dopo la stringa: le stringhe per oggetti equivalenti al 100% possono essere diverse. Sfortunatamente lo sforzo per ottenere una stringificazione deterministica sembra considerevole, pacchetto di esempio: github.com/substack/json-stable-stringify/blob/master/index.js
Mörre

1
@ Mörre - Credo di non essere stato abbastanza specifico. Con "non gestisce gli array", volevo dire che gli array sono danneggiati, non lasciati non ordinati. Il problema è perché typeof arr === "object"trasformerebbe gli array in oggetti. Ad esempio, var obj = {foo: ["bar", "baz"]}sarebbe stringa in{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych

per risolvere il problema riscontrato da @JonathanGawrych:if(obj.constructor.prototype !== Object.prototype)
ricka

3

È possibile ordinare l'oggetto in base al nome della proprietà in EcmaScript 2015

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}

Forse sarebbe un nome migliore sortObjectPropertiesByName()o più semplice sortPropertiesByName().
Jan

3

Ho preso la risposta da @Jason Parham e ho apportato alcuni miglioramenti

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

Ciò risolve il problema della conversione degli array in oggetti e consente inoltre di definire come ordinare gli array.

Esempio:

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

produzione:

{content:[{id:1},{id:2},{id:3}]}

2

La risposta accettata non funziona per me per gli oggetti nidificati per qualche motivo. Questo mi ha portato a programmare il mio. Dato che è la fine del 2019 quando scrivo questo, ci sono alcune altre opzioni disponibili nella lingua.

Aggiornamento: credo che la risposta di David Furlong sia un approccio preferibile al mio tentativo precedente, e l'ho respinto. Il mio si basa sul supporto per Object.entries (...), quindi nessun supporto per 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);

-

MANTENENDO QUESTA VERSIONE VECCHIA PER RIFERIMENTO STORICO

Ho scoperto che funzionerà un array semplice e piatto di tutte le chiavi dell'oggetto. In quasi tutti i browser (non Edge o Internet Explorer, prevedibilmente) e Node 12+ c'è una soluzione abbastanza breve ora che Array.prototype.flatMap (...) è disponibile. (Anche l'equivalente lodash funzionerebbe.) Ho testato solo in Safari, Chrome e Firefox, ma non vedo motivo per cui non funzionerebbe da nessun'altra parte che supporti flatMap e 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);
}

Con questo, puoi stringere senza dipendenze di terze parti e persino passare il tuo algoritmo di ordinamento che ordina le coppie di voci chiave-valore, in modo da poter ordinare per chiave, payload o una combinazione dei due. Funziona per oggetti annidati, array e qualsiasi combinazione di vecchi tipi di dati.

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

Stampe:

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

Se è necessario disporre della compatibilità con i motori JavaScript precedenti , è possibile utilizzare queste versioni leggermente più dettagliate che emulano il comportamento di flatMap. Il client deve supportare almeno ES5, quindi non Internet Explorer 8 o inferiore.

Questi restituiranno lo stesso risultato come sopra.

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

1

Funziona con lodash, oggetti annidati, qualsiasi valore dell'attributo oggetto:

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)

Si basa sul comportamento di Chrome e di Node secondo cui la prima chiave assegnata a un oggetto viene emessa per prima da JSON.stringify.


2
Grazie, ma ho avuto questo problema 4 anni fa, sono felice di dire che sono andato avanti un po 'da allora :)
Innovine

1
:) Mi fa piacere sentirlo. Anche se ieri ho avuto questo problema e non ho trovato la risposta che stavo cercando nella tua domanda (vecchia ma ancora pertinente) :)
AJP

0

Provare:

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

Grazie, ma a quanto pare l'ordine non è definito e funziona solo. Approccio interessante però!
Innovine

0

Ho creato una funzione per ordinare gli oggetti, e con callback .. che effettivamente creano un nuovo oggetto

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;
    },{});
}

e chiamalo con oggetto.

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

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

JSON.stringify( result )

che stampa {"value":"msio","os":"windows","name":"anu"}e per l'ordinamento con valore.

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

JSON.stringify( result )

che stampa {"os":"windows","value":"msio","name":"anu"}


1
Funziona bene, ma sfortunatamente non è ricorsivo.
Jonathan Gawrych

0

Se gli oggetti nell'elenco non hanno le stesse proprietà, genera un oggetto principale combinato prima di stringere:

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

// esempio come sotto. in ogni livello, per oggetti come {}, appiattito nell'ordinamento della chiave. per array, numeri o stringhe, appiattito come / con 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} "


Spiega perché usare una libreria esterna e, come ha detto @DeKaNszn, aggiungi alcune spiegazioni e un'introduzione. Sarebbe apprezzato.
Benj

questa funzione è copiata dal mio progetto. in cui uso underscore.js.
saintthor

0

Estendere la risposta di AJP, per gestire gli array:

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

Sorpreso nessuno ha menzionato la isEqualfunzione di lodash .

Esegue un confronto approfondito tra due valori per determinare se sono equivalenti.

Nota: questo metodo supporta il confronto di array, buffer di array, valori booleani, oggetti data, oggetti errore, mappe, numeri, oggetti oggetto, espressioni regolari, set, stringhe, simboli e array digitati. Gli oggetti oggetto vengono confrontati in base alle loro proprietà enumerabili, non ereditate. Funzioni e nodi DOM vengono confrontati in base a una rigorosa uguaglianza, ovvero ===.

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

Con il problema originale - le chiavi ordinate in modo incoerente - è un'ottima soluzione - e ovviamente si fermerà solo se trova un conflitto invece di serializzare ciecamente l'intero oggetto.

Per evitare di importare l'intera libreria, fai questo:

import { isEqual } from "lodash-es";

Esempio bonus: puoi anche usarlo con RxJS con questo operatore personalizzato

export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => 
                                                pipe(distinctUntilChanged(isEqual));

0

Dopotutto, ha bisogno di un Array che memorizzi nella cache tutte le chiavi nell'oggetto annidato (altrimenti ometterà le chiavi non memorizzate nella cache). La risposta più vecchia è semplicemente sbagliata, perché il secondo argomento non si preoccupa della notazione del punto. Quindi, la risposta (usando Set) diventa.

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

0

Ecco un approccio clone ... clona l'oggetto prima di convertirlo in 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;
}

Se è molto nuovo ma sembra funzionare bene sui file package.json.


-3

C'è un Array.sortmetodo che può esserti utile. Per esempio:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});

1
Tuttavia, non desidero ordinare l'array. In realtà la parte dell'array non è importante .. Vorrei ordinare le proprietà di un oggetto .. da alcuni rapidi esperimenti sembra che le proprietà siano elencate nell'ordine in cui sono state create. Un modo potrebbe essere quello di creare un nuovo oggetto e copiare le proprietà in ordine alfabetico, ma spero che ci sia qualcosa di più semplice / veloce ..
Innovine

1
@Innovine, anche questo non funzionerebbe poiché non è garantito che le chiavi vengano ordinate entro l'ora di creazione dalle specifiche.
Dogbert

Quindi la domanda diventa, come stringo i miei oggetti in modo tale che le chiavi siano in un ordine fisso .. non è impossibile per me fare per (key in obj) e memorizzarli in un array temporaneo e ordinarlo e manualmente costruire una stringa .. ma spero disperatamente che ci sia un modo più semplice
Innovine

No, questo è più o meno il modo per farlo. Benvenuto in JavaScript.
marksyzm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.