сортировка свойств объекта и JSON.преобразовать в строки
мое приложение имеет большой массив объектов, которые я строю и сохраняю на диск. К сожалению, когда объекты в массиве обрабатываются, а иногда и заменяются, свойства объектов перечислены в разных порядках (их порядок создания?). Когда я делаю JSON.stringify () на массиве и сохраните его, diff показывает свойства, перечисленные в разных порядках, что раздражает при попытке объединить данные дальше с помощью инструментов diff и слияния.
В Идеале Я необходимо отсортировать свойства объектов в алфавитном порядке перед выполнением операции stringify или как часть операции stringify. Существует код для манипулирования объектами массива во многих местах, и изменить их, чтобы всегда создавать свойства в явном порядке, было бы сложно.
предложения были бы очень приветствуются!
краткий пример:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
выходные данные этих двух вызовов stringify отличаются и отображаются в различном виде мои данные,но мое приложение не заботится о порядке свойств.. объекты построены во многих отношениях и местах..
17 ответов:
более простой, современный и в настоящее время браузер поддерживается подход просто так:
JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());однако этот метод удаляет все вложенные объекты, на которые нет ссылок и которые не применяются к объектам в массивах. Вы также захотите сгладить объект сортировки, если вы хотите что-то вроде этого вывода:
{"a":{"h":4,"z":3},"b":2,"c":1}вы можете сделать это с помощью этого:
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());чтобы сделать это программно с чем-то вы можете настроить себя, вам нужно нажать кнопку имена свойств объекта в массив, затем отсортируйте массив в алфавитном порядке и повторите этот массив (который будет находиться в правильном порядке) и выберите каждое значение из объекта в этом порядке. "hasOwnProperty" также проверяется, поэтому у вас определенно есть только собственные свойства объекта. Вот пример:
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 });опять же, это должно гарантировать, что вы перебираете в алфавитном порядке.
наконец, принимая его дальше для самого простого способа, эта библиотека будет рекурсивно позволяет сортировать любой JSON, который вы передаете в него: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));выход
{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
Я думаю, что если вы контролируете поколение JSON (и похоже, что это так), то для ваших целей это может быть хорошим решением:json-stable-stringify
с сайта проекта:
детерминированный JSON.stringify () с пользовательской сортировкой, чтобы получить детерминированные хэши из строковых результатов
Если созданный JSON является детерминированным, вы должны легко различать / объединять его.
вы можете передать сортированный массив имен свойств в качестве второго аргумента
JSON.stringify():JSON.stringify(obj, Object.keys(obj).sort())
обновление 2018-7-24:
эта версия сортирует вложенные объекты и поддерживает массива, а также:
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)); }тест:
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}]'); }); });
устаревший ответ:
краткая версия в ES2016. Кредит на @codename, от https://stackoverflow.com/a/29622653/94148
function orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
Это то же самое, что и ответ Сатпала Сингха
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"}"
Вы можете добавить
toJSONфункция для вашего объекта, который можно использовать для настройки вывода. Внутри функции, при добавлении текущие свойства для нового объекта в определенном порядке должны сохранить порядок, когда строковые.смотрите здесь:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify
нет встроенного метода управления заказом, потому что данные JSON предназначены для доступа по ключу.
вот jsfiddle с небольшим примером:
попробуйте закомментировать
toJSONфункция - порядок свойств меняется на противоположный. Обратите внимание, что это может быть специфичным для браузера, т. е. заказ официально не поддерживается в спецификации. Он работает в текущей версии Firefox, но если вы хотите 100% надежное решение, возможно, вам придется написать свой собственный stringifier функция.Edit:
тоже вижу это так вопрос, касающийся преобразовать в строки по недетерминированный вывод, особенно детали Дафф по поводу отличий браузера:
как детерминистически проверить, что объект JSON не был изменен?
рекурсивный и упрощенный ответ:
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 и сделал некоторые улучшения
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; }это устраняет проблему преобразования массивов в объекты, а также позволяет определить, как сортировать массивы.
пример:
var data = { content: [{id: 3}, {id: 1}, {id: 2}] }; sortObject(data, (i1, i2) => i1.id - i2.id)выход:
{content:[{id:1},{id:2},{id:3}]}
работает с lodash, вложенными объектами, любым значением атрибута объекта:
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)он зависит от поведения Chrome и узла, что первый ключ, назначенный объекту, выводится первым
JSON.stringify.
попробуй:
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);
Я сделал функцию для сортировки объекта, и с обратного вызова .. которые на самом деле создают новый объект
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; },{}); }и вызовите его с объектом .
var obj = { name : "anu", os : "windows", value : 'msio', }; var result = sortObj( obj , function( a, b ){ return a.key < b.key ; }); JSON.stringify( result ), который печатает
{"value":"msio","os":"windows","name":"anu"}, и для сортировки с значение .var result = sortObj( obj , function( a, b ){ return a.value < b.value ; }); JSON.stringify( result ), который печатает
{"os":"windows","value":"msio","name":"anu"}
вы можете сортировать объект по имени свойства в EcmaScript 2015
function sortObjectByPropertyName(obj) { return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {}); }
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;
если объекты в списке не имеют одинаковых свойств, создайте объединенный главный объект перед 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() );
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 ); }// пример как ниже. в каждом слое, для таких объектов, как {}, сглажены в сортировке ключей. для массивов, чисел или строк, сглаженных как/с JSON.преобразовать в строки.
FlatternInSort( { с:9, В: { Г: 4, З: 2, Д: 9 }, Ф:4, Гостей:[{х:8, ч:3},{а:3,Б:7}] } )
" {"F": 4,"a": [{"h": 3,"j": 8}, {"a": 3,"b": 7}],"b": {"e": 9,"y": 4,"z": 2},"c": 9}"
расширение ответа AJP, чтобы обрабатывать массивы:
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; }
здесь
Array.sortметод, который может быть полезным для вас. Например:yourBigArray.sort(function(a,b){ //custom sorting mechanism });
Comments