排序对象属性和 JSON.stringify
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16167581/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
sort object properties and JSON.stringify
提问by Innovine
My application has a large array of objects, which I stringify and save them to the disk. Unfortunately, when the objects in the array are manipulated, and sometimes replaced, the properties on the objects are listed in different orders (their creation order?). When I do JSON.stringify() on the array and save it, a diff shows the properties getting listed in different orders, which is annoying when trying to merge the data further with diff and merging tools.
我的应用程序有大量对象,我将它们字符串化并将它们保存到磁盘。不幸的是,当数组中的对象被操作,有时被替换时,对象的属性以不同的顺序列出(它们的创建顺序?)。当我对数组执行 JSON.stringify() 并保存它时,差异显示以不同顺序列出的属性,这在尝试使用差异和合并工具进一步合并数据时很烦人。
Ideally I would like to sort the properties of the objects in alphabetical order prior to performing the stringify, or as part of the stringify operation. There is code for manipulating the array objects in many places, and altering these to always create properties in an explicit order would be difficult.
理想情况下,我想在执行字符串化之前或作为字符串化操作的一部分按字母顺序对对象的属性进行排序。在许多地方都有用于操作数组对象的代码,并且更改这些代码以始终以明确的顺序创建属性将是困难的。
Suggestions would be most welcome!
建议将是最受欢迎的!
A condensed example:
一个浓缩的例子:
obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);
The output of these two stringify calls are different, and showing up in a diff of my data, but my application doesn't care about the ordering of properties. The objects are constructed in many ways and places.
这两个 stringify 调用的输出不同,并显示在我的数据的差异中,但我的应用程序并不关心属性的顺序。对象以多种方式和位置构建。
采纳答案by marksyzm
The simpler, modern and currently browser supported approach is simply this:
更简单、现代且当前浏览器支持的方法很简单:
JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());
However, this method does remove any nested objects that aren't referenced and does not apply to objects within arrays. You will want to flatten the sorting object as well if you want something like this output:
但是,此方法会删除任何未引用且不适用于数组中的对象的嵌套对象。如果您想要这样的输出,您还需要展平排序对象:
{"a":{"h":4,"z":3},"b":2,"c":1}
You can do that with this:
你可以这样做:
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());
To do it programmatically with something you can tweak yourself, you need to push the object property names into an array, then sort the array alphabetically and iterate through that array (which will be in the right order) and select each value from the object in that order. "hasOwnProperty" is checked also so you definitely have only the object's own properties. Here's an example:
要使用您可以自己调整的东西以编程方式执行此操作,您需要将对象属性名称推送到一个数组中,然后按字母顺序对数组进行排序并遍历该数组(按正确的顺序)并从对象中选择每个值那个命令。“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
});
Again, this should guarantee that you iterate through in alphabetical order.
同样,这应该保证您按字母顺序迭代。
Finally, taking it further for the simplest way, this library will recursively allow you to sort any JSON you pass into it: https://www.npmjs.com/package/json-stable-stringify
最后,以最简单的方式更进一步,该库将递归地允许您对传递给它的任何 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));
Output
输出
{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
回答by Stijn de Witt
I think that if you are in control of the JSON generation (and it sounds like you are), then for your purposes this might be a good solution: json-stable-stringify
我认为,如果您可以控制 JSON 生成(听起来像您一样),那么就您的目的而言,这可能是一个很好的解决方案:json-stable-stringify
From the project website:
从项目网站:
deterministic JSON.stringify() with custom sorting to get deterministic hashes from stringified results
确定性 JSON.stringify() 使用自定义排序从字符串化结果中获取确定性哈希
If the JSON produced is deterministic you should be able to easily diff/merge it.
如果生成的 JSON 是确定性的,您应该能够轻松地对其进行差异/合并。
回答by Christian d'Heureuse
You can pass a sorted array of the property names as the second argument of JSON.stringify():
您可以将属性名称的排序数组作为 的第二个参数传递JSON.stringify():
JSON.stringify(obj, Object.keys(obj).sort())
回答by Jor
I don't understand why the complexity of the current best answers is needed, to get all the keys recursively. Unless perfect performance is needed, it seems to me that we can just call JSON.stringify()twice, the first time to get all the keys, and the second time, to really do the job. That way, all the recursion complexity is handled by stringify, and we know that it knows its stuff, and how to handle each object type?:
我不明白为什么需要当前最佳答案的复杂性,以递归方式获取所有密钥。除非需要完美的性能,在我看来,我们可以只调用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 );
}
回答by aleung
Update 2018-7-24:
2018-7-24 更新:
This version sorts nested objects and supports array as well:
此版本对嵌套对象进行排序并支持数组:
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));
}
Test case:
测试用例:
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}]');
});
});
Deprecated answer:
弃用的答案:
A concise version in ES2016. Credit to @codename , from https://stackoverflow.com/a/29622653/94148
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), {}));
}
回答by David Furlong
https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490
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;
回答by Giridhar C R
This is same as Satpal Singh's answer
这与 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"}"
回答by Mayki Nayki
You can sort object by property name in EcmaScript 2015
您可以在 EcmaScript 2015 中按属性名称对对象进行排序
function sortObjectByPropertyName(obj) {
return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}
回答by Peter
I took the answer from @Jason Parham and made some improvements
我从@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;
}
This fixes the issue of arrays being converted to objects, and it also allows you to define how to sort arrays.
这修复了数组转换为对象的问题,并且还允许您定义如何对数组进行排序。
Example:
例子:
var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)
output:
输出:
{content:[{id:1},{id:2},{id:3}]}
回答by Jason Parham
A recursive and simplified answer:
递归和简化的答案:
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);

