Objekteigenschaften und JSON.stringify sortieren

91

Meine Anwendung verfügt über eine große Anzahl von Objekten, die ich anordne und auf der Festplatte speichere. Wenn die Objekte im Array bearbeitet und manchmal ersetzt werden, werden die Eigenschaften der Objekte leider in verschiedenen Reihenfolgen aufgelistet (ihre Erstellungsreihenfolge?). Wenn ich JSON.stringify () für das Array ausführe und es speichere, zeigt ein Diff die Eigenschaften an, die in verschiedenen Reihenfolgen aufgelistet werden. Dies ist ärgerlich, wenn versucht wird, die Daten mit Diff- und Zusammenführungswerkzeugen weiter zusammenzuführen.

Idealerweise möchte ich die Eigenschaften der Objekte in alphabetischer Reihenfolge sortieren, bevor ich die Zeichenfolge ausführe, oder als Teil der Zeichenfolgeoperation. Es gibt Code zum Bearbeiten der Array-Objekte an vielen Stellen, und es wäre schwierig, diese zu ändern, um Eigenschaften immer in einer expliziten Reihenfolge zu erstellen.

Vorschläge wären herzlich willkommen!

Ein komprimiertes Beispiel:

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

Die Ausgabe dieser beiden Stringify-Aufrufe ist unterschiedlich und wird in einem Unterschied meiner Daten angezeigt, aber meine Anwendung kümmert sich nicht um die Reihenfolge der Eigenschaften. Die Objekte sind auf viele Arten und Orte konstruiert.

Innovine
quelle
Bitte geben Sie ein Beispiel für das Objekt, das Sie zu stringifizieren versuchen (entweder JSON-Ausgabe oder JS-Objektliteral)
Bojangles
4
Für Objektschlüssel in Objekten wird keine feste Reihenfolge garantiert. Dies ist beabsichtigt.
Passant
1
@ Rab, wo hast du das herausgefunden? Hinweis: Es könnte funktionieren (und funktioniert wahrscheinlich die meiste Zeit), aber ich meinte, dass dies nicht garantiert ist.
Dogbert
1
OP hier. Hier gab es keine Abhängigkeit von der Eigenschaftsreihenfolge, nur eine Frage, wie Unterschiede in den serialisierten Daten vermieden werden können. Dies wurde schließlich gelöst, indem die Eigenschaften in Arrays versteckt und vor der Serialisierung sortiert wurden, ähnlich wie in der unten akzeptierten Antwort.
Innovine

Antworten:

71

Der einfachere, modernere und derzeit vom Browser unterstützte Ansatz lautet einfach:

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

Diese Methode entfernt jedoch alle verschachtelten Objekte, auf die nicht verwiesen wird, und gilt nicht für Objekte in Arrays. Sie sollten das Sortierobjekt auch reduzieren, wenn Sie so etwas wie diese Ausgabe möchten:

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

Das können Sie damit machen:

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

Um dies programmgesteuert mit etwas zu tun, das Sie selbst optimieren können, müssen Sie die Objekteigenschaftsnamen in ein Array verschieben, dann das Array alphabetisch sortieren und durch dieses Array (das in der richtigen Reihenfolge angeordnet ist) iterieren und jeden Wert aus dem Objekt in auswählen diese Reihenfolge. "hasOwnProperty" ist ebenfalls aktiviert, sodass Sie definitiv nur die eigenen Eigenschaften des Objekts haben. Hier ist ein Beispiel:

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

Auch dies sollte sicherstellen, dass Sie in alphabetischer Reihenfolge durchlaufen.

Mit dieser Bibliothek können Sie auf einfachste Weise rekursiv nach JSON sortieren, das Sie an sie übergeben: 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));

Ausgabe

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
marksyzm
quelle
9
Dies ist sehr viel, was ich zu vermeiden versucht habe :) Aber danke, scheint die richtige, wenn auch schwere Lösung zu sein.
Innovine
1
Dieser Code enthält einen Fehler. Sie können obj[i]in der for-Schleife nicht referenzieren , da ies sich um eine Ganzzahl handelt und die Eigenschaftsnamen nicht unbedingt (daher diese Frage). Es sollte sein obj[arr[i]].
Andrew Ensley
1
Rückrufe sind nicht ausschließlich für asynchrones Verhalten.
Marksyzm
2
@Johann Durch die Verwendung eines Rückrufs wird er iterateObjectAlphabeticallyzu einer wiederverwendbaren Funktion. Ohne den Rückruf müsste sich der 'Payload Code' in der Funktion selbst befinden. Ich denke, es ist eine sehr elegante Lösung, die in vielen Bibliotheken und im JS-Kern selbst verwendet wird. ZB Array.forEach .
Stijn de Witt
1
@marksyzm Es ist okay, in meinem Fall musste ich nur eine Auswahl von Feldern stringifizieren. Ihre Antwort hat mich also auf den Weg gebracht. Danke
Benj
39

Ich denke, wenn Sie die Kontrolle über die JSON-Generation haben (und es klingt so, als ob Sie es sind), könnte dies für Ihre Zwecke eine gute Lösung sein: json-Stable-Stringify

Von der Projektwebsite:

deterministisches JSON.stringify () mit benutzerdefinierter Sortierung, um deterministische Hashes aus stringifizierten Ergebnissen zu erhalten

Wenn der erzeugte JSON deterministisch ist, sollten Sie ihn leicht unterscheiden / zusammenführen können.

Stijn de Witt
quelle
Beachten Sie, dass dieses Repo in einem schlechten Zustand ist: Es wurde seit 3 ​​Jahren nicht mehr aktualisiert und hat ungelöste Probleme und Pull-Anfragen. Ich denke, es ist besser, sich einige der neueren Antworten anzuschauen.
Tom
3
@ Tom Beachten Sie auch, dass dieses Repo 5 Millionen wöchentliche (!) Downloads hat. Mit anderen Worten, es wird aktiv genutzt. Ungelöste offene Probleme und / oder Pull-Anfragen zu haben, bedeutet nicht viel. Nur dass sich die Autoren nicht um diese Themen kümmern oder keine Zeit haben usw. Nicht jede Bibliothek muss alle paar Monate aktualisiert werden. Tatsächlich erhalten die besten Bibliotheken imho sehr selten Updates. Wenn etwas getan ist, ist es getan. Ich sage nicht, dass dieses Projekt keine wirklichen Probleme hat, aber wenn ja, weisen Sie bitte darauf hin. Ich habe übrigens keine Beteiligung an dieser Bibliothek.
Stijn de Witt
ES6 Ich denke, es ist kaputt gegangen, Objekte (Nicht-Arrays) wie angegeben sortieren zu können. Ich brauche dies für die Benutzerbearbeitung. Auch wenn die Reihenfolge für den Computer keine Rolle spielt. Dies gilt für die Benutzer, die es bearbeiten. Es war einen Versuch wert.
TamusJRoyce
Zusätzlich zu den Aussagen von @Tom möchte ich darauf hinweisen, dass dieses Projekt keine Laufzeitabhängigkeiten aufweist. Das heißt, wenn dieses Repo keine Sicherheits- / Wartungsprobleme aufweist, ist es wahrscheinlich ziemlich stabil und sicher zu verwenden. Außerdem habe ich es gerade mit ES6 getestet und es scheint gut zu funktionieren (zumindest in Firefox).
Phil
27

Sie können ein sortiertes Array der Eigenschaftsnamen als zweites Argument übergeben JSON.stringify():

JSON.stringify(obj, Object.keys(obj).sort())
Christian d'Heureuse
quelle
1
Gibt es eine dokumentierte Garantie für dieses Verhalten?
Rich Remer
4
@richremer Ja, im ECMAScript-Standard wird der zweite Parameter von JSON.stringify()aufgerufen replacerund kann ein Array sein. Es wird verwendet, um ein zu erstellen, PropertyListdas dann verwendet wird SerializeJSONObject(), um die Eigenschaften in dieser Reihenfolge anzuhängen. Dies ist seit ECMAScript 5 dokumentiert.
Christian d'Heureuse
12
Beachten Sie, dass, wenn obj verschachtelte Objekte enthält, die verschachtelten Schlüssel auch im zweiten Argument vorhanden sein müssen. Andernfalls werden sie fallen gelassen! > JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' Gegenbeispiel : Eine (hackige) Möglichkeit, die Liste aller relevanten Schlüssel zu erstellen, besteht darin, das Objekt mit JSON.stringify zu durchlaufen: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } Beispiel:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim
Object.keys () ist nicht rekursiv. Dies funktioniert nicht für Objekte, die verschachtelte Arrays oder Objekte enthalten.
Jor
22

Ich verstehe nicht, warum die Komplexität der aktuell besten Antworten erforderlich ist, um alle Schlüssel rekursiv zu erhalten. Wenn keine perfekte Leistung erforderlich ist, können wir meines Erachtens nur JSON.stringify()zweimal anrufen , das erste Mal, um alle Schlüssel zu erhalten, und das zweite Mal, um die Arbeit wirklich zu erledigen. Auf diese Weise wird die gesamte Rekursionskomplexität von behandelt stringify, und wir wissen, dass sie sich auskennt und wie mit jedem Objekttyp umgegangen wird:

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
quelle
Das ist interessant, aber Sie haben tatsächlich mit stringify "betrogen", um alle Schlüssel einem Array zuzuordnen. Dieses Array kann einfacher, besser und sicherer über Object.keys
Z. Khullah
4
Nein, der Punkt ist, dass dies Object.keys()nicht rekursiv ist. Wenn Sie also ein Objekt wie haben {a: "A", b: {c: "C"} }und Object.keys darauf aufrufen, erhalten Sie nur Schlüssel aund b, aber nicht c. JSON.stringifyweiß alles über die Rekursion für jeden Objekttyp, der vom JSON-Serialisierungsprozess verarbeitet wird, sei es ein objektähnlicher oder ein Array (und es implementiert bereits die Logik, um beide zu erkennen), daher sollte alles für uns korrekt behandelt werden.
Jor
1
Ich liebe diese Lösung, sehr elegant und ausfallsicher, wie es scheint.
Markus
14

Update 2018-7-24:

Diese Version sortiert verschachtelte Objekte und unterstützt auch Arrays:

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

Testfall:

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

  });

Veraltete Antwort:

Eine prägnante Version in ES2016. Gutschrift an @codename von 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
quelle
Syntax nicht ganz richtig. Sollte sein -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Ben
Dies hat mein Problem behoben, bei dem C # DataContractJsonSerializer und "__type" nicht zuerst in der JSON-Zeichenfolge aufgeführt sind. Vielen Dank.
Joghurt der Weise
2
Beachten Sie, dass sich dies wie Christians Antwort bei verschachtelten Objekten schlecht verhält.
Daniel Griscom
sortObjPropertiesByKey ist nicht definiert. Wollten Sie sortObjByKey verwenden?
Paul Lynch
Es scheint nicht, dass dies nach sortObjByKey()Zirkelverweisen sucht. Seien Sie also vorsichtig, es kann abhängig von den Eingabedaten in einer Endlosschleife hängen bleiben. Beispiel: 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
quelle
1
Dies ist wahrscheinlich die beste Antwort da draußen. Danke @ David
acido
4

Dies ist die gleiche Antwort wie 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
quelle
3

Sie können eine benutzerdefinierte hinzufügen toJSON Ihrem Objekt Funktion der Sie die Ausgabe anpassen können. Innerhalb der Funktion sollte das Hinzufügen aktueller Eigenschaften zu einem neuen Objekt in einer bestimmten Reihenfolge diese Reihenfolge beim Stringifizieren beibehalten.

Siehe hier:

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

Es gibt keine integrierte Methode zur Steuerung der Reihenfolge, da auf JSON-Daten über Schlüssel zugegriffen werden soll.

Hier ist eine jsfiddle mit einem kleinen Beispiel:

http://jsfiddle.net/Eq2Yw/

Versuchen Sie, die toJSONFunktion auskommentieren - die Reihenfolge der Eigenschaften ist umgekehrt. Bitte beachten Sie, dass dies möglicherweise browserspezifisch ist, dh die Bestellung wird in der Spezifikation nicht offiziell unterstützt. Es funktioniert in der aktuellen Version von Firefox, aber wenn Sie eine 100% robuste Lösung wünschen, müssen Sie möglicherweise Ihre eigene Stringifier-Funktion schreiben.

Bearbeiten:

Siehe auch diese SO-Frage zur nicht deterministischen Ausgabe von stringify, insbesondere zu Daffs Details zu Browserunterschieden:

Wie kann deterministisch überprüft werden, ob ein JSON-Objekt nicht geändert wurde?

Dave R.
quelle
Die Eigenschaften jedes Objekts sind bekannt (und meistens Zeichenfolgen), so dass das Hardcodieren der Eigenschaften in meinem eigenen toSON-Stringifizierer möglicherweise viel schneller ist als das Sortieren ...!
Innovine
Das 'bestellteJsonStringify' unten hat fast funktioniert. Aber das scheint eine bessere Lösung für mich zu sein. Wenn ich '__type' des C # DataContractJsonSerializer erhalten muss, um das erste Element in der json-Zeichenfolge zu sein. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' usw. .....]); ein wenig Schmerz, alle Schlüssel auflisten zu müssen.
Joghurt der Weise
3

Eine rekursive und vereinfachte Antwort:

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
quelle
reordersollte umbenannt werden in sortObjectund dies behandelt keine Arrays
Jonathan Gawrych
Vielen Dank, dass Sie dies bemerkt haben. Das Problem von OP bestand darin, Objekte und keine Arrays neu zu sortieren.
Jason Parham
@ JonathanGawrych Warum sollten Sie Arrays sowieso sortieren, sie sollen eine Bestellung haben. Ein Array [1,2,3] ist nicht dasselbe wie ein Array [2,3,1], aber ein Objekt hat keine Eigenschaftsreihenfolge. Das Problem ist, dass die Reihenfolge nach der Stringifizierung plötzlich wichtig ist - die Zeichenfolgen für 100% äquivalente Objekte können unterschiedlich sein. Leider scheint der Aufwand für eine deterministische Stringifizierung beträchtlich, Beispielpaket: github.com/substack/json-stable-stringify/blob/master/index.js
Mörre
1
@ Mörre - Ich glaube ich war nicht spezifisch genug. Mit "behandelt keine Arrays" meinte ich, dass Arrays kaputt sind und nicht unsortiert bleiben. Das Problem ist, dass typeof arr === "object"Arrays in Objekte umgewandelt werden. Zum Beispiel var obj = {foo: ["bar", "baz"]}würde in{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych
um das von @JonathanGawrych gefundene Problem zu beheben:if(obj.constructor.prototype !== Object.prototype)
Ricka
3

Sie können Objekte in EcmaScript 2015 nach Eigenschaftsnamen sortieren

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
Mayki Nayki
quelle
Vielleicht wäre ein besserer Name sortObjectPropertiesByName()oder einfacher sortPropertiesByName().
Jan
3

Ich habe die Antwort von @Jason Parham genommen und einige Verbesserungen vorgenommen

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

Dies behebt das Problem, dass Arrays in Objekte konvertiert werden, und Sie können auch festlegen, wie Arrays sortiert werden sollen.

Beispiel:

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

Ausgabe:

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

Die akzeptierte Antwort funktioniert bei mir für verschachtelte Objekte aus irgendeinem Grund nicht. Dies führte mich dazu, meine eigenen zu codieren. Da es Ende 2019 ist, als ich dies schreibe, gibt es einige weitere Optionen in der Sprache.

Update: Ich glaube, die Antwort von David Furlong ist ein bevorzugter Ansatz für meinen früheren Versuch, und das habe ich abgehört. Meins ist auf die Unterstützung von Object.entries (...) angewiesen, daher keine Internet Explorer-Unterstützung.

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

- -

BEHALTEN SIE DIESE ÄLTERE VERSION FÜR HISTORISCHE REFERENZEN

Ich habe festgestellt, dass eine einfache, flache Anordnung aller Schlüssel im Objekt funktioniert. In fast allen Browsern (vorhersehbar nicht Edge oder Internet Explorer) und Node 12+ gibt es jetzt, da Array.prototype.flatMap (...) verfügbar ist, eine ziemlich kurze Lösung . (Das lodash-Äquivalent würde auch funktionieren.) Ich habe nur in Safari, Chrome und Firefox getestet, aber ich sehe keinen Grund, warum es nirgendwo anders funktionieren würde, das flatMap und Standard JSON.stringify (...) unterstützt .

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

Auf diese Weise können Sie ohne Abhängigkeiten von Drittanbietern eine Zeichenfolge erstellen und sogar Ihren eigenen Sortieralgorithmus übergeben, der die Schlüssel-Wert-Eintragspaare sortiert, sodass Sie nach Schlüssel, Nutzlast oder einer Kombination aus beiden sortieren können. Funktioniert für verschachtelte Objekte, Arrays und jede Mischung aus einfachen alten Datentypen.

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

Drucke:

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

Wenn Sie mit älteren JavaScript-Engines kompatibel sein müssen , können Sie diese etwas ausführlicheren Versionen verwenden, die das flatMap-Verhalten emulieren. Der Client muss mindestens ES5 unterstützen, daher kein Internet Explorer 8 oder niedriger.

Diese geben das gleiche Ergebnis wie oben zurück.

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
quelle
1

Funktioniert mit lodash, verschachtelten Objekten und jedem Wert des Objektattributs:

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)

Es basiert auf dem Verhalten von Chrome und Node, dass der erste einem Objekt zugewiesene Schlüssel zuerst von ausgegeben wird JSON.stringify.

AJP
quelle
2
Danke, aber ich hatte dieses Problem vor 4 Jahren, ich bin froh zu sagen, dass ich seitdem ein bisschen weitergezogen bin :)
Innovine
1
:) Gut, das zu hören. Obwohl ich gestern dieses Problem hatte und die gesuchte Antwort unter Ihrer (alten, aber immer noch relevanten) Frage nicht gefunden habe :)
AJP
0

Versuchen:

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
quelle
Danke, aber anscheinend ist die Bestellung undefiniert und funktioniert nur zufällig. Interessanter Ansatz!
Innovine
0

Ich habe eine Funktion zum Sortieren von Objekten und mit Rückruf erstellt, die tatsächlich ein neues Objekt erstellt

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

und nenne es mit Objekt.

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

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

JSON.stringify( result )

welche druckt {"value":"msio","os":"windows","name":"anu"}, und zum Sortieren mit Wert.

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

JSON.stringify( result )

welche druckt {"os":"windows","value":"msio","name":"anu"}

rab
quelle
1
Funktioniert gut, ist aber leider nicht rekursiv.
Jonathan Gawrych
0

Wenn Objekte in der Liste nicht dieselben Eigenschaften haben, generieren Sie vor dem Stringifizieren ein kombiniertes Masterobjekt:

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

// Beispiel wie unten. in jeder Ebene für Objekte wie {} in Schlüsselsortierung abgeflacht. für Arrays, Zahlen oder Strings, abgeflacht wie / mit 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} "

Heiliger
quelle
Bitte erläutern Sie, warum Sie eine externe Bibliothek verwenden, und fügen Sie, wie @DeKaNszn sagte, einige Erklärungen und eine Einführung hinzu. Es wäre dankbar.
Benj
Diese Funktion wurde aus meinem Projekt kopiert. in dem ich underscore.js benutze.
Saintthor
0

Erweiterung der Antwort von AJP auf Arrays:

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
quelle
0

Überrascht hat niemand die isEqualFunktion von lodash erwähnt .

Führt einen umfassenden Vergleich zwischen zwei Werten durch, um festzustellen, ob sie gleichwertig sind.

Hinweis: Diese Methode unterstützt das Vergleichen von Arrays, Array-Puffern, Booleschen Werten, Datumsobjekten, Fehlerobjekten, Karten, Zahlen, Objektobjekten, regulären Ausdrücken, Mengen, Zeichenfolgen, Symbolen und typisierten Arrays. Objektobjekte werden anhand ihrer eigenen, nicht vererbten, aufzählbaren Eigenschaften verglichen. Funktionen und DOM-Knoten werden durch strikte Gleichheit verglichen, dh ===.

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

Mit dem ursprünglichen Problem - Schlüssel werden uneinheitlich geordnet - ist es eine großartige Lösung - und natürlich hört es einfach auf, wenn es einen Konflikt findet, anstatt das gesamte Objekt blind zu serialisieren.

Um zu vermeiden, dass die gesamte Bibliothek importiert wird, gehen Sie wie folgt vor:

import { isEqual } from "lodash-es";

Bonusbeispiel: Sie können dies auch mit RxJS mit diesem benutzerdefinierten Operator verwenden

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

Schließlich benötigt es ein Array, das alle Schlüssel im verschachtelten Objekt zwischenspeichert (andernfalls werden die nicht zwischengespeicherten Schlüssel weggelassen). Die älteste Antwort ist einfach falsch, da sich das zweite Argument nicht um die Punktnotation kümmert. So wird die Antwort (mit Set).

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
quelle
0

Hier ist ein Klonansatz ... Klonen Sie das Objekt, bevor Sie es in json konvertieren:

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

If ist sehr neu, scheint aber bei package.json-Dateien gut zu funktionieren.

Corey Alix
quelle
-3

Es gibt eine Array.sortMethode, die für Sie hilfreich sein kann. Beispielsweise:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});
Egor4eg
quelle
1
Ich möchte das Array jedoch nicht sortieren. Eigentlich ist der Array-Teil nicht wichtig. Ich möchte die Eigenschaften eines Objekts sortieren. Aus ein paar kurzen Experimenten geht hervor, dass die Eigenschaften in der Reihenfolge aufgelistet sind, in der sie erstellt wurden. Eine Möglichkeit könnte sein, ein neues Objekt zu erstellen und die Eigenschaften in alphabetischer Reihenfolge zu kopieren, aber ich hoffe, dass es etwas einfacheres / schnelleres gibt.
Innovine
1
@Innovine, auch das würde nicht funktionieren, da nicht garantiert wird, dass die Schlüssel nach Erstellungszeit nach Spezifikation sortiert werden.
Dogbert
Dann stellt sich die Frage, wie ich meine Objekte so anordne, dass die Schlüssel in einer festen Reihenfolge vorliegen. Es ist mir nicht unmöglich, dies zu tun (obj eingeben) und sie in einem temporären Array zu speichern und diese und manuell zu sortieren konstruiere einen String .. aber ich hoffe verzweifelt, dass es einen einfacheren Weg gibt
Innovine
Nein, das ist so ziemlich der Weg, es zu tun. Willkommen bei JavaScript.
Marksyzm