【问题标题】:Sorting JavaScript Object by key value Recursively以递归方式按键值对 JavaScript 对象进行排序
【发布时间】:2014-07-08 10:51:28
【问题描述】:

按值也是对象的键对对象进行排序,并对内部对象进行排序,即递归地对对象进行排序。排序应该按照键。

我查看了 * 的其他问题,但 没有一个是针对 对象递归排序的。

我调查的问题:

Sorting JavaScript Object by property value

示例:

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

输出

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级对象层次需要排序。

【问题讨论】:

  • Javascript 对象没有顺序,它们是一个键值映射。你不能订购它们。
  • 修正了标题:它说的是“属性”而不是键(不能用于排序,正如@deceze 指出的那样)
  • 为什么我们不能按字母顺序对键进行排序?
  • 正如这个答案中所指出的:*.com/a/5525812/2964675 它不在 ECMAScript 规范中。如果您想要某种顺序,请尝试使用数组对象。
  • @MarcoCI @_deceze 这个链接指向浏览器的问题,我检查了当我在 chrome 的控制台中创建一个对象时,它给了我一个已经排序的对象,但我想使用 对一个对象进行排序node.js.

标签: javascript node.js sorting recursion


【解决方案1】:

我认为@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
}

【讨论】:

  • 当使用for..in 循环键时,没有任何东西可以保证键的顺序。它可能会保持创建对象的顺序,但同样不能保证,并且在不同的 Javascript 实现中可能会完全不同。
  • @deceze 我已经用 Chrome 测试了这个,因此它可以在 Node.js 中工作。我将在其他浏览器中尝试并在此处发布。我知道这不是 ECMA 标准的一部分,但它对于 Node.js 应用程序来说是一个足够好的解决方案,特别是在我认为 ksr89 试图实现 ORM 的地方。
  • 它的键需要排序的object 可能在列表/数组中。此函数不会对其进行排序。
【解决方案2】:

我在这个页面上写了以下信息。该代码基于 Gaurav Ramanan 的回答,但对数组和 null 的处理方式不同。

比较 JSON

要比较来自 JSON 文件的数据,您可能希望它们的格式相同

  • 来自javascript:JSON.stringify(JSON.parse(jsonString), null, '\t') 最后一个参数也可以是多个空格 最后 2 个参数是可选的(如果不存在则缩小输出)
  • 来自 Visual Studio Code:使用 Prettify JSON 扩展

验证缩进(即 TAB)和行尾(即 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')

【讨论】:

    【解决方案3】:

    上述解决方案仅适用于 node.js 的当前实现细节。

    ECMAScript standard doesn't guarantee any order 用于 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]);
    }
    

    【讨论】:

      【解决方案4】:

      由于最近恢复了这一点,我认为值得再次指出的是,我们通常应该将对象视为无序的属性集合。尽管 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)), {})
      

      虽然这行得通,但逗号运算符的添加和属性赋值的使用让我觉得它更难看。但任何一个都应该工作。

      【讨论】:

        【解决方案5】:

        跟进@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 循环中使用,在那里它将随着最终排序结果发生变异。

        输出将是一个递归排序的对象。

        【讨论】: