ordenar las propiedades del objeto y JSON.stringify

94

Mi aplicación tiene una gran variedad de objetos, que clasifico y guardo en el disco. Desafortunadamente, cuando los objetos de la matriz se manipulan y, a veces, se reemplazan, las propiedades de los objetos se enumeran en diferentes órdenes (¿su orden de creación?). Cuando hago JSON.stringify () en la matriz y la guardo, una diferencia muestra las propiedades que se enumeran en diferentes órdenes, lo cual es molesto cuando trato de fusionar los datos más con las diferencias y las herramientas de fusión.

Idealmente, me gustaría ordenar las propiedades de los objetos en orden alfabético antes de realizar la secuenciación, o como parte de la operación de secuenciación. Hay código para manipular los objetos de la matriz en muchos lugares, y sería difícil alterarlos para crear siempre propiedades en un orden explícito.

¡Cualquier sugerencia será bienvenida!

Un ejemplo condensado:

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

La salida de estas dos llamadas de stringify es diferente y se muestra en una diferencia de mis datos, pero mi aplicación no se preocupa por el orden de las propiedades. Los objetos se construyen de muchas formas y lugares.

Innovine
fuente
Por favor, dé un ejemplo del objeto que está tratando de codificar (ya sea salida JSON o literal de objeto JS)
Bojangles
4
No se garantiza que las claves de objeto en los objetos tengan un orden fijo. Esto es por diseño.
Passerby
1
@rab, ¿dónde averiguaste eso? Nota: podría funcionar (y probablemente lo hace la mayor parte del tiempo), pero quise decir que no está garantizado.
Dogbert
1
OP aquí. Aquí no hubo dependencia del orden de propiedad, solo una pregunta sobre cómo evitar diferencias en los datos serializados. Esto finalmente se resolvió almacenando las propiedades en matrices y clasificándolas antes de la serialización, al igual que en la respuesta aceptada a continuación.
Innovine

Respuestas:

73

El enfoque más simple, moderno y actualmente compatible con el navegador es simplemente este:

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

Sin embargo, este método elimina los objetos anidados a los que no se hace referencia y no se aplica a los objetos dentro de las matrices. También querrá aplanar el objeto de clasificación si desea algo como este resultado:

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

Puedes hacer eso con esto:

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

Para hacerlo programáticamente con algo que puede modificar usted mismo, debe insertar los nombres de las propiedades del objeto en una matriz, luego ordenar la matriz alfabéticamente e iterar a través de esa matriz (que estará en el orden correcto) y seleccionar cada valor del objeto en ese orden. "hasOwnProperty" también está marcado, por lo que definitivamente solo tiene las propiedades propias del objeto. He aquí un ejemplo:

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

Nuevamente, esto debería garantizar que se repita en orden alfabético.

Finalmente, yendo más allá de la manera más simple, esta biblioteca le permitirá de forma recursiva ordenar cualquier JSON que le pase: 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));

Salida

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
marksyzm
fuente
9
Esto es mucho lo que estaba tratando de evitar :) Pero gracias, parece la solución correcta, aunque pesada.
Innovine
1
Hay un error en este código. No puede hacer referencia obj[i]en el bucle for porque ies un número entero y los nombres de propiedad no lo son necesariamente (de ahí esta pregunta). Debería serlo obj[arr[i]].
Andrew Ensley
1
Las devoluciones de llamada no son exclusivas del comportamiento asincrónico.
marksyzm
2
@Johann Al usar una devolución de llamada aquí, se convierte iterateObjectAlphabeticallyen una función reutilizable. Sin la devolución de llamada, el 'código de carga útil' tendría que estar dentro de la propia función. Creo que es una solución muy elegante y que se usa en muchas bibliotecas y en el propio núcleo de JS. Por ejemplo, Array.forEach .
Stijn de Witt
1
@marksyzm Está bien, en mi caso tuve que clasificar solo una selección de campos. Por lo tanto, su respuesta me puso en camino. Gracias
Benj
39

Creo que si tiene el control de la generación JSON (y parece que lo tiene), entonces, para sus propósitos, esta podría ser una buena solución: json-stable-stringify

Desde el sitio web del proyecto:

JSON.stringify () determinista con clasificación personalizada para obtener hashes deterministas a partir de resultados en cadena

Si el JSON producido es determinista, debería poder diferenciarlo / fusionarlo fácilmente.

Stijn de Witt
fuente
Tenga en cuenta que ese repositorio está en mal estado: no se ha actualizado en 3 años y tiene problemas sin resolver y solicitudes de extracción. Creo que es mejor buscar algunas de las respuestas más nuevas.
Tom
3
@Tom También tenga en cuenta que ese repositorio tiene 5 millones de descargas semanales (!). En otras palabras, se está utilizando activamente. Tener problemas abiertos 'sin resolver' y / o solicitudes de extracción no significa mucho. Solo que los autores no se preocupan por esos problemas o no tienen tiempo, etc. No todas las bibliotecas necesitan actualizarse cada dos meses. De hecho, en mi humilde opinión, las mejores bibliotecas reciben actualizaciones con muy poca frecuencia. Cuando se hace algo, se hace. No estoy diciendo que este proyecto no tenga problemas reales, pero si es así, indíquelos. No tengo ninguna implicación con esta lib BTW.
Stijn de Witt
ES6 Supongo que se rompió al poder ordenar objetos (no matrices), como se especifica. Necesito esto para la edición del usuario. Incluso si el orden no le importa a la computadora. Lo hace con los usuarios que lo editan. Valió la pena intentarlo.
TamusJRoyce
Además de lo que dice @Tom, también señalaré que este proyecto no tiene dependencias de tiempo de ejecución. Lo que significa que si no hay problemas de seguridad / mantenimiento en este repositorio, probablemente sea bastante estable y seguro de usar. Además, lo acabo de probar con ES6 y parece funcionar bien (al menos en Firefox).
Phil
27

Puede pasar una matriz ordenada de los nombres de propiedad como segundo argumento de JSON.stringify():

JSON.stringify(obj, Object.keys(obj).sort())
Christian d'Heureuse
fuente
1
¿Existe algún tipo de garantía documentada sobre este comportamiento?
rich remer
4
@richremer Sí, en el estándar ECMAScript , JSON.stringify()se llama al segundo parámetro de replacery puede ser una matriz. Se usa para construir un PropertyListque luego se usa SerializeJSONObject()para agregar las propiedades en ese orden. Esto está documentado desde ECMAScript 5.
Christian d'Heureuse
14
Tenga en cuenta que si obj tiene objetos anidados, las claves anidadas también deben estar presentes en el segundo argumento; de lo contrario, se eliminarán. Contraejemplo: > JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}' una forma (hacky) de crear la lista de todas las claves relevantes es usar JSON.stringify para atravesar el objeto: function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); } Ejemplo:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Saif Hakim
Object.keys () no es recursivo. Esto no funcionará para ningún objeto que contenga matrices u objetos anidados.
Jor
25

No entiendo por qué se necesita la complejidad de las mejores respuestas actuales para obtener todas las claves de forma recursiva. A menos que se necesite un rendimiento perfecto, me parece que podemos llamar JSON.stringify()dos veces, la primera para obtener todas las claves y la segunda para hacer realmente el trabajo. De esa manera, toda la complejidad de la recursividad es manejada por stringify, y sabemos que conoce sus cosas y cómo manejar cada tipo de objeto:

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
fuente
eso es interesante, pero en realidad has "engañado" usando stringify para asignar todas las claves a una matriz. Esa matriz puede ser más fácil, mejor y más segura obtenida a través de Object.keys
Z. Khullah
5
No, el punto es que Object.keys()no es recursivo, por lo que si tiene un objeto como {a: "A", b: {c: "C"} }y llama a Object.keys en él, solo obtendrá las claves ay b, pero no c. JSON.stringifysabe todo sobre la recursividad en cada tipo de objeto manejado por el proceso de serialización JSON, ya sea un objeto o una matriz (y ya implementa la lógica para reconocer ambos), por lo que debería manejar todo correctamente para nosotros.
Jor
1
Me encanta esta solución, parece muy elegante y a prueba de fallos.
Markus
14

Actualización 2018-7-24:

Esta versión clasifica objetos anidados y también admite matrices:

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

Caso de prueba:

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

  });

Respuesta obsoleta:

Una versión concisa en ES2016. Crédito a @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
fuente
La sintaxis no es del todo correcta. Debería ser -function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Ben
Esto, por ahora, ha solucionado mi problema con C # DataContractJsonSerializer y "__type" no aparece en primer lugar en la cadena json. Gracias.
Yogurt The Wise
2
Tenga en cuenta que esto, como la respuesta de Christian, se comporta mal con los objetos anidados.
Daniel Griscom
sortObjPropertiesByKey no está definido. ¿Quiso utilizar sortObjByKey?
Paul Lynch
No parece que esto sortObjByKey()compruebe las referencias circulares, así que tenga cuidado, puede colgarse en un bucle infinito dependiendo de los datos de entrada. Ejemplo: var objA = {}; var objB = {objA: objA}; objA.objB = objB;-> sortObjByKey(objA);->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
Klesun
4

Esto es lo mismo que la respuesta 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
fuente
3

Puede agregar una toJSONfunción personalizada a su objeto que puede usar para personalizar la salida. Dentro de la función, agregar propiedades actuales a un nuevo objeto en un orden específico debería preservar ese orden cuando se cadena.

Mira aquí:

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

No hay un método incorporado para controlar los pedidos porque los datos JSON deben ser accedidos por claves.

Aquí hay un jsfiddle con un pequeño ejemplo:

http://jsfiddle.net/Eq2Yw/

Intente comentar la toJSONfunción: el orden de las propiedades se invierte. Tenga en cuenta que esto puede ser específico del navegador, es decir, que la especificación no admite oficialmente el pedido. Funciona en la versión actual de Firefox, pero si desea una solución 100% robusta, es posible que deba escribir su propia función de secuenciador.

Editar:

También vea esta pregunta de SO sobre la salida no determinista de stringify, especialmente los detalles de Daff sobre las diferencias del navegador:

¿Cómo verificar determinísticamente que un objeto JSON no ha sido modificado?

Dave R.
fuente
Las propiedades de cada objeto son conocidas (y en su mayoría cadenas), por lo que codificar las propiedades en mi propio stringifier toJSON podría ser mucho más rápido que ordenar ...!
Innovine
El 'orderJsonStringify' a continuación casi funcionó. Pero esta parece ser una mejor solución para mí. Cuando necesito obtener '__type' de C # DataContractJsonSerializer para que sea el primer elemento en la cadena json. var json = JSON.stringify (myjsonobj, ['__type', 'id', 'text', 'somesubobj' etc .....]); un poco de dolor tener lista todas las claves.
Yogurt The Wise
3

Una respuesta recursiva y simplificada:

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
fuente
reorderdebe cambiarse el nombre a sortObjecty esto no maneja matrices
Jonathan Gawrych
Gracias por notar eso, y el problema de OP era recurrir a objetos, no a matrices.
Jason Parham
@JonathanGawrych ¿Por qué ordenarías las matrices de todos modos? Se supone que deben tener un orden. Una matriz [1,2,3] no es lo mismo que una matriz [2,3,1], pero un objeto no tiene orden de propiedad. El problema es que el orden de repente importa después de la cadena: las cadenas para objetos 100% equivalentes pueden ser diferentes. Desafortunadamente, el esfuerzo por obtener una cadena de caracteres determinista parece considerable, paquete de ejemplo: github.com/substack/json-stable-stringify/blob/master/index.js
Mörre
1
@ Mörre - Creo que no fui lo suficientemente específico. Por "no maneja matrices", me refiero a que las matrices están rotas, no se dejan sin clasificar. El problema es porque typeof arr === "object"transformaría matrices en objetos. Por ejemplo var obj = {foo: ["bar", "baz"]}, estaría encadenado en{ "foo": { "0": "bar", "1": "baz"} }
Jonathan Gawrych
para solucionar el problema encontrado por @JonathanGawrych:if(obj.constructor.prototype !== Object.prototype)
ricka
3

Puede ordenar el objeto por nombre de propiedad en EcmaScript 2015

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
Mayki Nayki
fuente
Quizás un nombre mejor sería sortObjectPropertiesByName()o más simple sortPropertiesByName().
Ene
3

Tomé la respuesta de @Jason Parham e hice algunas mejoras

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

Esto soluciona el problema de que las matrices se conviertan en objetos y también le permite definir cómo ordenar las matrices.

Ejemplo:

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

salida:

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

La respuesta aceptada no me funciona para objetos anidados por alguna razón. Esto me llevó a codificar el mío. Como es a fines de 2019 cuando escribo esto, hay algunas opciones más disponibles dentro del idioma.

Actualización: Creo que la respuesta de David Furlong es un enfoque preferible a mi intento anterior, y lo he descartado. El mío depende del soporte para Object.entries (...), por lo que no es compatible con 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);

-

MANTENER ESTA VERSIÓN ANTIGUA PARA REFERENCIA HISTÓRICA

Descubrí que una matriz simple y plana de todas las claves en el objeto funcionará. En casi todos los navegadores (no en Edge o Internet Explorer, como era de esperar) y Node 12+, hay una solución bastante corta ahora que Array.prototype.flatMap (...) está disponible. (El equivalente de lodash también funcionaría). Solo he probado en Safari, Chrome y Firefox, pero no veo ninguna razón por la que no funcione en ningún otro lugar que admita flatMap y JSON.stringify estándar (...) .

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 esto, puede secuenciar sin dependencias de terceros e incluso pasar su propio algoritmo de ordenación que ordena los pares de entrada clave-valor, de modo que pueda ordenar por clave, carga útil o una combinación de los dos. Funciona para objetos anidados, matrices y cualquier combinación de tipos de datos antiguos.

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

Huellas dactilares:

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

Si debe tener compatibilidad con motores JavaScript más antiguos , puede usar estas versiones un poco más detalladas que emulan el comportamiento de flatMap. El cliente debe admitir al menos ES5, por lo que no Internet Explorer 8 o inferior.

Estos devolverán el mismo resultado que el anterior.

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

Funciona con lodash, objetos anidados, cualquier valor de atributo de objeto:

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)

Depende del comportamiento de Chrome y Node que la primera clave asignada a un objeto se genere primero por JSON.stringify.

AJP
fuente
2
Gracias, pero tuve este problema hace 4 años, me complace decir que he avanzado un poco desde entonces :)
Innovine
1
:) Que bueno oírlo. Aunque tuve este problema ayer y no encontré la respuesta que estaba buscando en su pregunta (antigua pero aún relevante) :)
AJP
0

Tratar:

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
fuente
Gracias, pero aparentemente el pedido no está definido y solo funciona. ¡Aunque es un enfoque interesante!
Innovine
0

Hice una función para ordenar el objeto y con devolución de llamada ... que en realidad crea un nuevo objeto

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

y llámalo con objeto.

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

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

JSON.stringify( result )

que imprime {"value":"msio","os":"windows","name":"anu"}y para clasificar con valor.

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

JSON.stringify( result )

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

rab
fuente
1
Funciona bien, pero desafortunadamente no es recursivo.
Jonathan Gawrych
0

Si los objetos de la lista no tienen las mismas propiedades, genere un objeto maestro combinado antes de encadenar:

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

// ejemplo como a continuación. en cada capa, para objetos como {}, aplanados en orden de clave. para matrices, números o cadenas, aplanadas como / 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} "

Saintthor
fuente
Explique por qué se utiliza una biblioteca externa y, como dijo @DeKaNszn, agregue algunas explicaciones y una introducción. Será apreciado.
Benj
esta función se copia de mi proyecto. en el que utilizo underscore.js.
saintthor
0

Extendiendo la respuesta de AJP, para manejar matrices:

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

Sorprendido que nadie haya mencionado la isEqualfunción de Lodash .

Realiza una comparación profunda entre dos valores para determinar si son equivalentes.

Nota: Este método admite la comparación de matrices, búferes de matriz, valores booleanos, objetos de fecha, objetos de error, mapas, números, objetos de objeto, expresiones regulares, conjuntos, cadenas, símbolos y matrices escritas. Los objetos objeto se comparan por sus propias propiedades enumerables, no heredadas. Las funciones y los nodos DOM se comparan por estricta igualdad, es decir ===.

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

Con el problema original, las claves están ordenadas de manera inconsistente, es una gran solución y, por supuesto, se detendrá si encuentra un conflicto en lugar de serializar ciegamente todo el objeto.

Para evitar importar toda la biblioteca, haga esto:

import { isEqual } from "lodash-es";

Ejemplo de bonificación: también puede usar esto con RxJS con este operador personalizado

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

Después de todo, necesita una matriz que almacene en caché todas las claves en el objeto anidado (de lo contrario, omitirá las claves no almacenadas en caché). La respuesta más antigua es simplemente incorrecta, porque el segundo argumento no se preocupa por la notación de puntos. Entonces, la respuesta (usando Set) se convierte en.

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

Aquí hay un enfoque de clonación ... clone el objeto antes de convertirlo a 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 es muy nuevo pero parece funcionar bien en archivos package.json.

Corey Alix
fuente
-3

Hay un Array.sortmétodo que puede resultarle útil. Por ejemplo:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});
Egor4eg
fuente
1
Sin embargo, no deseo ordenar la matriz. En realidad, la parte de la matriz no es importante. Deseo ordenar las propiedades de un objeto. De algunos experimentos rápidos, parece que las propiedades se enumeran en el orden en que se crean. Una forma podría ser crear un nuevo objeto y copiar las propiedades en orden alfabético, pero espero que haya algo más fácil / rápido ..
Innovine
1
@Innovine, incluso eso no funcionaría ya que las claves no están garantizadas para ser ordenadas por la especificación en el momento de la creación.
Dogbert
Entonces la pregunta es, ¿cómo puedo secuenciar mis objetos de tal manera que las claves estén en un orden fijo? construir una cadena ... pero espero desesperadamente que haya una manera más fácil
Innovine
No, esa es prácticamente la forma de hacerlo. Bienvenido a JavaScript.
marksyzm