按键值递归排序JavaScript对象
Sorting JavaScript Object by key value Recursively
按值也是对象的键对对象进行排序,并对该内部对象进行排序,即对对象进行递归排序。按键排序
我查看了Stackoverflow的其他问题,但是没有是针对对象递归排序的。
我调查的问题:
按属性值排序JavaScript对象
例子:
input = {
"Memo": {
"itemAmount1": "5",
"taxName1": "TAX",
"productPrice1": "10",
"accountName1": "Account Receivable (Debtors)"
},
"Footer": {
"productDescription2": "Maggie",
"itemQuantity2": "49.5",
"accountName2": "Account Receivable (Debtors)",
"taxName2": "TAX"
},
"Header": {
"itemDiscount3": "10",
"accountName3": "Account Receivable (Debtors)",
"productPrice3": "10",
"taxName3": "TAX"
}
}
输出strong>
output = {
"Footer": {
"accountName2": "Account Receivable (Debtors)",
"itemQuantity2": "49.5",
"productDescription2": "Maggie",
"taxName2": "TAX"
},
"Header": {
"accountName3": "Account Receivable (Debtors)",
"itemDiscount3": "10",
"productPrice3": "10",
"taxName3": "TAX"
},
"Memo": {
"accountName1": "Account Receivable (Debtors)",
"itemAmount1": "5",
"productPrice1": "10",
"taxName1": "TAX"
}
}
不一定是2级对象层次,可以包含n级对象层次需要排序。
我认为@ksr89的意思是当我们应用for - in循环时,我们按顺序获得键。我认为这是一个有效的用例,特别是在基于Node.js的orm开发中
下面的函数应该工作,我认为是你正在寻找的。
input = {
"Memo": {
"itemAmount1": "5",
"taxName1": "TAX",
"productPrice1": "10",
"accountName1": "Account Receivable (Debtors)"
},
"Footer": {
"productDescription2": "Maggie",
"itemQuantity2": "49.5",
"accountName2": "Account Receivable (Debtors)",
"taxName2": "TAX"
},
"Header": {
"itemDiscount3": "10",
"accountName3": "Account Receivable (Debtors)",
"productPrice3": "10",
"taxName3": "TAX"
}
}
window.sortedObject = sort(input);
function sort(object){
if (typeof object != "object" || object instanceof Array) // Not to sort the array
return object;
var keys = Object.keys(object);
keys.sort();
var newObject = {};
for (var i = 0; i < keys.length; i++){
newObject[keys[i]] = sort(object[keys[i]])
}
return newObject;
}
for (var key in sortedObject){
console.log (key);
//Prints keys in order
}
我在这一页上写了以下信息。该代码基于Gaurav Ramanan的答案,但处理数组和null的方式不同。
<标题>比较JSON h1>要比较JSON文件中的数据,您可能希望以相同的方式格式化它们
- from javascript:
JSON.stringify(JSON.parse(jsonString), null, ''t')
最后一个参数也可以是一些空格最后两个参数是可选的(如果没有则输出最小化) - 从Visual Studio Code:与美化JSON扩展
验证缩进(即制表符)和行结束(即Unix)。此外,在格式化过程中,键可以递归排序。
用javascript排序键:
const {isArray} = Array
const {keys} = Object
function sortKeysRec(obj) {
if (isArray(obj)) {
const newArray = []
for (let i = 0, l = obj.length; i < l; i++)
newArray[i] = sortKeysRec(obj[i])
return newArray
}
if (typeof obj !== 'object' || obj === null)
return obj
const sortedKeys = keys(obj).sort()
const newObject = {}
for (let i = 0, l = sortedKeys.length; i < l; i++)
newObject[sortedKeys[i]] = sortKeysRec(obj[sortedKeys[i]])
return newObject
}
确保unix行以javascript: jsonString.replace(/'r'n/ug, ''n')
结尾
根据@Gaurav Ramanan的回答,这里有一个更短的ES6方法:
function sort(obj) {
if (typeof obj !== "object" || Array.isArray(obj))
return obj;
const sortedObject = {};
const keys = Object.keys(obj).sort();
keys.forEach(key => sortedObject[key] = sort(obj[key]));
return sortedObject;
}
第一个条件只是确保您只解析一个有效的对象。因此,如果不是,它将立即返回原始值不变。
然后赋值一个空对象,因为它将在forEach循环中使用,在forEach循环中,它将根据最终排序结果进行变异。
输出将是一个递归排序对象。
上述解决方案仅适用于node.js的当前实现细节。
ECMAScript标准不保证keys
迭代的顺序。
也就是说,我能想到的唯一解决方案是使用数组作为支持对对象的properties
进行排序并对其进行迭代:
var keys = Object.keys(object);
keys.sort();
for (var i = 0; i < keys.length; i++){
// this won't break if someone change NodeJS or Chrome implementation
console.log(keys[i]);
}
由于这个问题最近被重新讨论,我认为有必要再次指出,我们通常应该将对象视为无序的属性集合。虽然ES6确实指定了键遍历顺序(主要是先添加到后添加的属性,但对于类似整数的键,情况有所不同),但如果这样的话,感觉好像是在滥用类型。如果是有序的,则使用数组。
也就是说,如果你决定这样做,那么在ES6中这是相对简单的:
const sortKeys = (o) =>
Object (o) !== o || Array .isArray (o)
? o
: Object .keys (o) .sort () .reduce ((a, k) => ({...a, [k]: sortKeys (o [k])}), {})
const input = {Memo: {itemAmount1: "5", taxName1: "TAX", productPrice1: "10", accountName1: "Account Receivable (Debtors)"}, Footer: {productDescription2: "Maggie", itemQuantity2: "49.5", accountName2: "Account Receivable (Debtors)", taxName2: "TAX"}, Header: {itemDiscount3: "10", accountName3: "Account Receivable (Debtors)", productPrice3: "10", taxName3: "TAX"}}
console .log (
sortKeys(input)
)
.as-console-wrapper {min-height: 100% !important; top: 0}
注意,这里有一个潜在的性能问题,正如Rich Snapp所描述的那样。如果它成为我的应用程序的瓶颈,我只会花时间修复它,但如果我们需要,我们可以用一个更像这样的版本来修复这个问题:
const sortKeys = (o) =>
Object (o) !== o || Array .isArray (o)
? o
: Object .keys (o) .sort () .reduce ((a, k) => ((a [k] = sortKeys (o [k]), a)), {})
虽然这可以工作,但在我看来,添加逗号操作符和使用属性分配使它更难看。但任何一种都应该有效。
这个经过测试的答案为递归问题提供了递归解决方案。注意,它不排序数组(这通常是不希望的),但排序数组中的对象(甚至嵌套数组)。
它使用lodash _.isPlainObject
来简化识别对象的逻辑,但是如果你不使用lodash,你可以用你自己的lodash替换它,它是一个对象吗?逻辑。
const sortObjectProps = obj => {
return Object.keys(obj).sort().reduce((ordered, key) => {
let value = obj[key]
if (_.isPlainObject(value)) {
ordered[key] = sortObjectProps(value)
} else {
if (Array.isArray(value)) {
value = value.map(v => {
if (_.isPlainObject(v)) v = sortObjectProps(v)
return v
})
}
ordered[key] = value
}
return ordered
}, {})
}
const input = {
"Memo": {
"itemAmount1": "5",
"taxName1": "TAX",
"productPrice1": "10",
"accountName1": "Account Receivable (Debtors)"
},
"Footer": {
"productDescription2": "Maggie",
"itemQuantity2": "49.5",
"accountName2": "Account Receivable (Debtors)",
"taxName2": "TAX"
},
"Header": {
"itemDiscount3": "10",
"accountName3": "Account Receivable (Debtors)",
"productPrice3": "10",
"taxName3": "TAX"
}
}
const expected = {
"Footer": {
"accountName2": "Account Receivable (Debtors)",
"itemQuantity2": "49.5",
"productDescription2": "Maggie",
"taxName2": "TAX"
},
"Header": {
"accountName3": "Account Receivable (Debtors)",
"itemDiscount3": "10",
"productPrice3": "10",
"taxName3": "TAX"
},
"Memo": {
"accountName1": "Account Receivable (Debtors)",
"itemAmount1": "5",
"productPrice1": "10",
"taxName1": "TAX"
}
}
const actual = sortObjectProps(input)
const success = JSON.stringify(actual) === JSON.stringify(expected)
console.log(JSON.stringify(actual))
console.log('success (actual is expected)', success)
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
对对象中的所有内容进行排序
是的,这包括嵌套对象、数组、对象的数组(以及对这些对象排序!)
我以@danday74的解决方案为起点,并使我的版本使用数组,嵌套在数组中的数组和嵌套在数组中的对象。
也就是说,甚至像这样:
const beforeSort = {
foo: {
b: 2,
a: [
{ b: 1, a: 10 },
{ y: 0, x: 5 },
],
},
};
变成这样:
const afterSort = {
foo: {
a: [
{ x: 5, y: 0 }, // See note
{ a: 10, b: 1 },
],
b: 2,
},
};
/**
* Note:
* This object goes first in this array because 5 < 10.
* Unlike objects sorting by keys; arrays of objects sort
* by value of the first property, not by the key of the first property.
* This was important for me because arrays of objects are typically
* the same kinds of objects, so sorting alphabetically by key would be
* pretty pointless. Instead, it sorts by the value.
*/
我的情况是,我需要比较一个对象(其数组的顺序无关紧要)和JSON.stringify()
'd对象的字符串。解析JSON为对象并在对象之间执行深度比较是不可能的,因为这些字符串在数据库中。
由于事物的顺序可能随机改变,我需要确保每次生成的JSON都完全相同。这意味着对对象中的所有内容进行排序,无论嵌套如何。
用上面的例子;对象beforeSort
:
// After running through JSON.stringify()...
'{"foo":{"b":2,"a":[{"b":1,"a":10},{"y":0,"x":5}]}}'
需要匹配afterSort
:
// After running through JSON.stringify()...
'{"foo":{"a":[{"x":5,"y":0},{"a":10,"b":1}],"b":2}}'
(相同的对象,不同的字符串。)
显然,如果数组的顺序对您很重要,那么这将没有帮助。
虽然……我现在没有心情看它,我想象我可以用一个简单的参数和一个战略性的if语句来打开和关闭数组排序。值得一试!
JavaScript版本(含测试对象)
// I use lodash's isEqual() is cloneDeep().
// Testing provided below.
function deepSortObject(object) {
const deepSort = (object) => {
// Null or undefined objects return immediately.
if (object == null) {
return object;
}
// Handle arrays.
if (Array.isArray(object)) {
return (
_.cloneDeep(object)
// Recursively sort each item in the array.
.map((item) => deepSort(item))
// Sort array itself.
.sort((a, b) => {
let workingA = a;
let workingB = b;
// Object or Array, we need to look at its first value...
if (typeof a === "object") {
workingA = a[Object.keys(a)[0]];
}
if (typeof b === "object") {
workingB = b[Object.keys(b)[0]];
}
if (Array.isArray(a)) {
workingA = a[0];
}
if (Array.isArray(b)) {
workingB = b[0];
}
// If either a or b was an object/array, we deep sort...
if (workingA !== a || workingB !== b) {
const sortedOrder = deepSort([workingA, workingB]);
if (_.isEqual(sortedOrder[0], workingA)) {
return -1;
} else {
return 1;
}
}
// If both were scalars, sort the normal way!
return a < b ? -1 : a > b ? 1 : 0;
})
);
}
// Anything other than Objects or Arrays just send it back.
if (typeof object != "object") {
return object;
}
// Handle objects.
const keys = Object.keys(object);
keys.sort();
const newObject = {};
for (let i = 0; i < keys.length; ++i) {
newObject[keys[i]] = deepSort(object[keys[i]]);
}
return newObject;
};
return deepSort(object);
}
// TESTING
const unsortedInput = {
ObjectC: {
propertyG_C: [[8, 7, 6], [5, 4, 3], [], [2, 1, 0]], // Array of arrays
propertyF_C: [
// This should result in sorting like: [2]'s a:0, [1]'s a:1, [0]'s a.x:5
{
b: 2,
a: [
{ b: 1, a: 10 }, // Sort array y by property a...
{ y: 0, x: 5 }, // vs property x
// Hot testing tip: change x to -1 and propertyF_C will sort it to the top!
],
},
{ c: 1, b: [1, 2, 0], a: 1 },
{ c: 0, b: [1, 2, 0], a: 0 },
],
propertyE_C: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_C: true,
propertyC_C: 1,
propertyD_C: [2, 0, 1],
propertyA_C: "Blah",
},
ObjectA: {
propertyE_A: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_A: true,
propertyC_A: 1,
propertyD_A: [2, 0, 1],
propertyA_A: "Blah",
},
ObjectB: {
propertyE_B: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_B: true,
propertyC_B: 1,
propertyD_B: [2, 0, 1],
propertyA_B: "Blah",
},
};
const sortedOutput = {
ObjectA: {
100: true,
200: false,
propertyA_A: "Blah",
propertyB_A: true,
propertyC_A: 1,
propertyD_A: [0, 1, 2],
propertyE_A: {
a: 1,
b: 2,
},
},
ObjectB: {
100: true,
200: false,
propertyA_B: "Blah",
propertyB_B: true,
propertyC_B: 1,
propertyD_B: [0, 1, 2],
propertyE_B: {
a: 1,
b: 2,
},
},
ObjectC: {
100: true,
200: false,
propertyA_C: "Blah",
propertyB_C: true,
propertyC_C: 1,
propertyD_C: [0, 1, 2],
propertyE_C: {
a: 1,
b: 2,
},
propertyF_C: [
{ a: 0, b: [0, 1, 2], c: 0 },
{ a: 1, b: [0, 1, 2], c: 1 },
{
a: [
{ x: 5, y: 0 },
{ a: 10, b: 1 },
],
b: 2,
},
],
propertyG_C: [[0, 1, 2], [3, 4, 5], [6, 7, 8], []],
},
};
// Some basic testing...
console.log("Before sort, are the JSON strings the same?", JSON.stringify(unsortedInput) === JSON.stringify(sortedOutput));
console.log("After sort, are the JSON stirngs the same?", JSON.stringify(deepSortObject(unsortedInput)) === JSON.stringify(sortedOutput));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<打印稿版本/h3>
/* eslint-disable @typescript-eslint/no-explicit-any */
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
/**
* Takes an object that may have nested properties and returns a new shallow
* copy of the object with the keys sorted. It also sorts arrays, and arrays of
* objects.
*
* IF THERE IS ANY IMPORTANCE IN THE ORDER OF YOUR ARRAYS DO NOT USE THIS.
*
* Use this in conjunction with JSON.strigify() to create consistent string
* representations of the same object, even if the order of properties or arrays
* might be different.
*
* And if you're wondering. Yes, modern JS does maintain order in objects:
* https://exploringjs.com/es6/ch_oop-besides-classes.html#_traversal-order-of-properties
*
* @param object
* @returns object
*/
export function deepSortObject(object: any) {
const deepSort = (object: any): any => {
// Null or undefined objects return immediately.
if (object == null) {
return object;
}
// Handle arrays.
if (Array.isArray(object)) {
return (
cloneDeep(object)
// Recursively sort each item in the array.
.map((item) => deepSort(item))
// Sort array itself.
.sort((a, b) => {
let workingA = a;
let workingB = b;
// Object or Array, we need to look at its first value...
if (typeof a === "object") {
workingA = a[Object.keys(a)[0]];
}
if (typeof b === "object") {
workingB = b[Object.keys(b)[0]];
}
if (Array.isArray(a)) {
workingA = a[0];
}
if (Array.isArray(b)) {
workingB = b[0];
}
// If either a or b was an object/array, we deep sort...
if (workingA !== a || workingB !== b) {
const sortedOrder = deepSort([workingA, workingB]);
if (isEqual(sortedOrder[0], workingA)) {
return -1;
} else {
return 1;
}
}
// If both were scalars, sort the normal way!
return a < b ? -1 : a > b ? 1 : 0;
})
);
}
// Anything other than Objects or Arrays just send it back.
if (typeof object != "object") {
return object;
}
// Handle objects.
const keys = Object.keys(object);
keys.sort();
const newObject: Record<string, unknown> = {};
for (let i = 0; i < keys.length; ++i) {
newObject[keys[i]] = deepSort(object[keys[i]]);
}
return newObject;
};
return deepSort(object);
}
单元测试import { deepSortObject } from "@utils/object";
const unsortedInput = {
ObjectC: {
propertyG_C: [[8, 7, 6], [5, 4, 3], [], [2, 1, 0]], // Array of arrays
propertyF_C: [
// This should result in sorting like: [2]'s a:0, [1]'s a:1, [0]'s a.x:5
{
b: 2,
a: [
{ b: 1, a: 10 }, // Sort array y by property a...
{ y: 0, x: 5 }, // vs property x
// Hot testing tip: change x to -1 and propertyF_C will sort it to the top!
],
},
{ c: 1, b: [1, 2, 0], a: 1 },
{ c: 0, b: [1, 2, 0], a: 0 },
],
propertyE_C: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_C: true,
propertyC_C: 1,
propertyD_C: [2, 0, 1],
propertyA_C: "Blah",
},
ObjectA: {
propertyE_A: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_A: true,
propertyC_A: 1,
propertyD_A: [2, 0, 1],
propertyA_A: "Blah",
},
ObjectB: {
propertyE_B: {
b: 2,
a: 1,
},
200: false,
100: true,
propertyB_B: true,
propertyC_B: 1,
propertyD_B: [2, 0, 1],
propertyA_B: "Blah",
},
};
const sortedOutput = {
ObjectA: {
100: true,
200: false,
propertyA_A: "Blah",
propertyB_A: true,
propertyC_A: 1,
propertyD_A: [0, 1, 2],
propertyE_A: {
a: 1,
b: 2,
},
},
ObjectB: {
100: true,
200: false,
propertyA_B: "Blah",
propertyB_B: true,
propertyC_B: 1,
propertyD_B: [0, 1, 2],
propertyE_B: {
a: 1,
b: 2,
},
},
ObjectC: {
100: true,
200: false,
propertyA_C: "Blah",
propertyB_C: true,
propertyC_C: 1,
propertyD_C: [0, 1, 2],
propertyE_C: {
a: 1,
b: 2,
},
propertyF_C: [
{ a: 0, b: [0, 1, 2], c: 0 },
{ a: 1, b: [0, 1, 2], c: 1 },
{
a: [
{ x: 5, y: 0 },
{ a: 10, b: 1 },
],
b: 2,
},
],
propertyG_C: [[0, 1, 2], [3, 4, 5], [6, 7, 8], []],
},
};
describe("object utils", () => {
describe("sortObjectByKeys()", () => {
test("should sort correctly", () => {
expect(JSON.stringify(deepSortObject(unsortedInput))).toEqual(
JSON.stringify(sortedOutput)
);
});
});
});
我的领导告诉我这可能值得清理和托管在NPM,我不知道。太懒惰。所以我把它贴在了这里
- Chrome开发工具(如何知道我在调用哪个javascript对象)
- 循环遍历以数组为值的Javascript对象
- 从ajax请求中获取javascript对象
- 如何从对象的原型方法访问JavaScript对象属性
- 将XML转换为普通的旧JavaScript对象
- 通过引用传递JavaScript对象
- javascript对象操作:根据指定条件选择属性
- Javascript对象类在单击时打开窗口进行颜色选择,并在更改时替换对象背景颜色
- 如何在异步函数中使用javascript对象
- 临时Javascript对象
- 如何在ASP中为用户控件添加Javascript对象网
- 使用数组向下搜索Javascript对象
- Rails将JavaScript对象存储到Model的有效方式
- JavaScript对象不是从原型链继承的
- 如何创建具有默认值的JavaScript对象字段?(AngularJS模型相关)
- SetInterval在javascript对象中表现怪异
- Javascript 对象和 this 关键字
- 如何在不知道关键字的情况下访问javascript对象值
- 在 JavaScript 对象中设置要使用的运算符的属性
- 如何搜索JavaScript对象并更改值