В моем приложении есть большой массив объектов, которые я структурирую и сохраняю на диск. К сожалению, когда объектами в массиве манипулируют, а иногда и заменяют, свойства объектов перечислены в разном порядке (порядок их создания?). Когда я выполняю JSON.stringify () в массиве и сохраняю его, diff показывает свойства, перечисленные в разном порядке, что раздражает при попытке объединить данные с помощью инструментов diff и слияния.
В идеале я хотел бы отсортировать свойства объектов в алфавитном порядке до выполнения строкового преобразования или как часть операции строкового преобразования. Есть код для управления объектами массива во многих местах, и изменить его, чтобы всегда создавать свойства в явном порядке, было бы сложно.
Предложения приветствуются!
Краткий пример:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
Вывод этих двух вызовов stringify различен и отображается в различии моих данных, но мое приложение не заботится об упорядочивании свойств. Объекты построены разными способами и в разных местах.
источник
Ответы:
Более простой, современный и поддерживаемый браузером подход заключается в следующем:
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}
источник
obj[i]
в цикле for, потому чтоi
это целое число, а имена свойств не обязательно (отсюда и этот вопрос). Так и должно бытьobj[arr[i]]
.iterateObjectAlphabetically
в функцию многократного использования. Без обратного вызова «код полезной нагрузки» должен был бы находиться внутри самой функции. Я думаю, что это очень элегантное решение, которое используется во многих библиотеках и самом ядре JS. Например, Array.forEach .Я думаю, что если вы контролируете генерацию JSON (а это похоже на вас), то для ваших целей это может быть хорошим решением: json-stable-stringify
С сайта проекта:
Если созданный JSON детерминирован, вы сможете легко его сравнить / объединить.
источник
Вы можете передать отсортированный массив имен свойств в качестве второго аргумента
JSON.stringify()
:JSON.stringify(obj, Object.keys(obj).sort())
источник
JSON.stringify()
вызываетсяreplacer
и может быть массивом. Он используется для создания,PropertyList
который затем используетсяSerializeJSONObject()
для добавления свойств в указанном порядке. Это задокументировано, начиная с ECMAScript 5.> JSON.stringify({a: {c: 1, b: 2}}, ['a']) '{"a":{}}'
один (хакерский) способ создать список всех соответствующих ключей - использовать JSON.stringify для обхода объекта:function orderedStringify(obj) { const allKeys = []; JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; }); return JSON.stringify(obj, allKeys.sort()); }
Пример:> orderedStringify({a: {c: 1, b: 2}}) '{"a":{"b":2,"c":1}}'
Я не понимаю, зачем нужна сложность текущих лучших ответов, чтобы рекурсивно получить все ключи. Если не требуется идеальная производительность, мне кажется, что мы можем просто позвонить
JSON.stringify()
дважды: в первый раз, чтобы получить все ключи, и во второй раз, чтобы действительно выполнить работу. Таким образом, вся сложность рекурсии обрабатываетсяstringify
, и мы знаем, что он знает свое дело и как обрабатывать каждый тип объекта: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 ); }
источник
Object.keys()
это не рекурсивно, поэтому, если у вас есть такой объект,{a: "A", b: {c: "C"} }
и вы вызываете на нем Object.keys, вы получите только ключиa
иb
, но неc
.JSON.stringify
знает все о рекурсии для каждого типа объекта, обрабатываемого процессом сериализации JSON, будь то объектный или массив (и он уже реализует логику для распознавания обоих), поэтому он должен обрабатывать все правильно для нас.Обновление 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 orderedJsonStringify(o) { return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {})); }
sortObjByKey()
проверяет циклические ссылки, поэтому будьте осторожны, это может зависнуть в бесконечном цикле в зависимости от входных данных. Пример:var objA = {}; var objB = {objA: objA}; objA.objB = objB;
->sortObjByKey(objA);
->VM59:6 Uncaught RangeError: Maximum call stack size exceeded
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;
источник
Это то же самое, что и ответ Сатпала Сингха
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:
http://jsfiddle.net/Eq2Yw/
Попробуйте закомментировать
toJSON
функцию - порядок свойств обратный. Имейте в виду, что это может зависеть от браузера, т. Е. Порядок заказа официально не поддерживается в спецификации. Он работает в текущей версии Firefox, но если вам нужно 100% надежное решение, вам, возможно, придется написать свою собственную функцию стрингификатора.Редактировать:
Также см. Этот вопрос SO относительно недетерминированного вывода stringify, особенно подробности Daff о различиях браузеров:
Как детерминированно проверить, что объект 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);
источник
reorder
следует переименовать в,sortObject
и это не обрабатывает массивыtypeof arr === "object"
он преобразует массивы в объекты. Например,var obj = {foo: ["bar", "baz"]}
было бы преобразовано в{ "foo": { "0": "bar", "1": "baz"} }
if(obj.constructor.prototype !== Object.prototype)
Вы можете сортировать объект по имени свойства в EcmaScript 2015
function sortObjectByPropertyName(obj) { return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {}); }
источник
sortObjectPropertiesByName()
или прощеsortPropertiesByName()
.Я взял ответ от @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}]}
источник
Принятый ответ по какой-то причине не работает для вложенных объектов. Это привело меня к написанию собственного кода. Поскольку я пишу это в конце 2019 года, на этом языке доступно еще несколько вариантов.
Обновление: я считаю, что ответ Дэвида Ферлонга является предпочтительным подходом к моей предыдущей попытке, и я проиграл это. Mine полагается на поддержку Object.entries (...), поэтому нет поддержки 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);
-
СОХРАНЕНИЕ ЭТОЙ СТАРЫЙ ВЕРСИИ ДЛЯ ИСТОРИЧЕСКОЙ СПРАВКИ
Я обнаружил, что простой плоский массив всех ключей объекта будет работать. Почти во всех браузерах (кроме Edge или Internet Explorer, как и ожидалось) и в Node 12+ теперь есть довольно короткое решение, когда доступен Array.prototype.flatMap (...) . (Эквивалент lodash тоже будет работать.) Я тестировал только в Safari, Chrome и Firefox, но не вижу причин, по которым он не работал бы где-либо еще, поддерживающем flatMap и стандартный JSON.stringify (...) .
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); }
Благодаря этому вы можете выполнить строковую обработку без сторонних зависимостей и даже передать свой собственный алгоритм сортировки, который сортирует пары записей ключ-значение, так что вы можете сортировать по ключу, полезной нагрузке или комбинации этих двух. Работает для вложенных объектов, массивов и любой смеси простых старых типов данных.
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));
Печать:
{ "a": 2, "b": 1, "c": { "x": 3, "y": [ 2048, 1999, { "f": 5, "g": "help", "x": false } ], "z": 4 } }
Если вам необходима совместимость со старыми механизмами JavaScript , вы можете использовать эти немного более подробные версии, имитирующие поведение flatMap. Клиент должен поддерживать как минимум ES5, поэтому нет Internet Explorer 8 или ниже.
Они вернут тот же результат, что и выше.
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); }
источник
Работает с 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 и Node, согласно которому первый ключ, назначенный объекту, выводится первым
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"}
источник
Если объекты в списке не имеют одинаковых свойств, сгенерируйте объединенный главный объект перед преобразованием в строку:
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.stringify.
источник
Расширение ответа 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; }
источник
Удивлен, что никто не упомянул о
isEqual
функции lodash .С исходной проблемой - ключи неправильно упорядочены - это отличное решение - и, конечно, оно просто остановится, если обнаружит конфликт, вместо слепой сериализации всего объекта.
Чтобы избежать импорта всей библиотеки, сделайте следующее:
import { isEqual } from "lodash-es";
Бонусный пример: вы также можете использовать это с RxJS с этим настраиваемым оператором
export const distinctUntilEqualChanged = <T>(): MonoTypeOperatorFunction<T> => pipe(distinctUntilChanged(isEqual));
источник
В конце концов, ему нужен массив, который кэширует все ключи во вложенном объекте (в противном случае он будет пропускать некэшированные ключи). Самый старый ответ просто неверен, потому что второй аргумент не заботится о точечной нотации. Итак, ответ (с помощью 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()) }
источник
Вот подход клонирования ... клонируйте объект перед преобразованием в 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; }
Если он очень новый, но, похоже, отлично работает с файлами package.json.
источник
Есть
Array.sort
метод, который может быть вам полезен. Например:yourBigArray.sort(function(a,b){ //custom sorting mechanism });
источник